Created
May 19, 2025 08:59
-
-
Save GeekBoySupreme/4ef74bc335007359c6a14aecbf45aaa6 to your computer and use it in GitHub Desktop.
To eliminate scrollbars from a page
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
// Create and add styles to hide default scrollbars without affecting layout | |
const style = document.createElement('style'); | |
style.textContent = ` | |
* { | |
scrollbar-width: none !important; /* Firefox */ | |
-ms-overflow-style: none !important; /* IE/Edge */ | |
} | |
*::-webkit-scrollbar { | |
width: 0px !important; | |
height: 0px !important; | |
display: none !important; /* Chrome/Safari/Opera */ | |
} | |
.virtual-scrollbar { | |
position: absolute; | |
right: 2px; | |
width: 8px; | |
border-radius: 4px; | |
background-color: rgba(0, 0, 0, 0.2); | |
transition: opacity 0.3s; | |
opacity: 0; | |
z-index: 9999; | |
pointer-events: none; | |
} | |
.virtual-scrollbar.horizontal { | |
bottom: 2px; | |
left: 0; | |
right: unset; | |
height: 8px; | |
width: auto; | |
} | |
.virtual-scrollbar-thumb { | |
position: absolute; | |
width: 8px; | |
border-radius: 4px; | |
background-color: rgba(0, 0, 0, 0.5); | |
cursor: pointer; | |
pointer-events: all; | |
} | |
.virtual-scrollbar-thumb.horizontal { | |
height: 8px; | |
} | |
.virtual-scrollbar-container { | |
position: relative !important; | |
} | |
.virtual-scrollbar:hover, | |
.virtual-scrollbar.active { | |
opacity: 1; | |
} | |
.virtual-scrollbar-thumb:hover, | |
.virtual-scrollbar-thumb.active { | |
background-color: rgba(0, 0, 0, 0.7); | |
} | |
`; | |
document.head.appendChild(style); | |
// Find all scrollable elements and add virtual scrollbars | |
function addVirtualScrollbars() { | |
const elements = Array.from(document.querySelectorAll('*')).filter(el => { | |
const style = window.getComputedStyle(el); | |
return ( | |
(style.overflow === 'auto' || style.overflow === 'scroll' || | |
style.overflowY === 'auto' || style.overflowY === 'scroll' || | |
style.overflowX === 'auto' || style.overflowX === 'scroll') && | |
(el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth) && | |
!el.classList.contains('virtual-scrollbar-container') | |
); | |
}); | |
elements.forEach(createScrollbarsFor); | |
} | |
function createScrollbarsFor(element) { | |
// Skip if already processed | |
if (element.getAttribute('data-has-virtual-scrollbar')) return; | |
element.setAttribute('data-has-virtual-scrollbar', 'true'); | |
// Mark the element as a scrollbar container | |
element.classList.add('virtual-scrollbar-container'); | |
// Create vertical scrollbar if needed | |
if (element.scrollHeight > element.clientHeight) { | |
const scrollbar = document.createElement('div'); | |
scrollbar.className = 'virtual-scrollbar'; | |
scrollbar.style.height = `${element.clientHeight}px`; | |
const thumb = document.createElement('div'); | |
thumb.className = 'virtual-scrollbar-thumb'; | |
// Calculate thumb height | |
const thumbHeight = Math.max( | |
30, | |
element.clientHeight * (element.clientHeight / element.scrollHeight) | |
); | |
thumb.style.height = `${thumbHeight}px`; | |
scrollbar.appendChild(thumb); | |
element.appendChild(scrollbar); | |
// Show scrollbar on hover | |
element.addEventListener('mouseenter', () => { | |
scrollbar.style.opacity = '1'; | |
setTimeout(() => { | |
if (!scrollbar.classList.contains('active')) { | |
scrollbar.style.opacity = '0'; | |
} | |
}, 1500); | |
}); | |
// Update thumb position on scroll | |
element.addEventListener('scroll', () => { | |
const scrollRatio = element.scrollTop / (element.scrollHeight - element.clientHeight); | |
const thumbTop = scrollRatio * (element.clientHeight - thumbHeight); | |
thumb.style.top = `${thumbTop}px`; | |
scrollbar.style.opacity = '1'; | |
clearTimeout(element.scrollTimer); | |
element.scrollTimer = setTimeout(() => { | |
if (!scrollbar.classList.contains('active')) { | |
scrollbar.style.opacity = '0'; | |
} | |
}, 1500); | |
}); | |
// Make thumb draggable | |
thumb.addEventListener('mousedown', (e) => { | |
e.preventDefault(); | |
scrollbar.classList.add('active'); | |
const startY = e.clientY; | |
const startTop = thumb.offsetTop; | |
const maxTop = element.clientHeight - thumbHeight; | |
function onMouseMove(e) { | |
const deltaY = e.clientY - startY; | |
const newTop = Math.max(0, Math.min(maxTop, startTop + deltaY)); | |
thumb.style.top = `${newTop}px`; | |
const scrollRatio = newTop / maxTop; | |
element.scrollTop = scrollRatio * (element.scrollHeight - element.clientHeight); | |
} | |
function onMouseUp() { | |
document.removeEventListener('mousemove', onMouseMove); | |
document.removeEventListener('mouseup', onMouseUp); | |
scrollbar.classList.remove('active'); | |
setTimeout(() => { | |
scrollbar.style.opacity = '0'; | |
}, 1500); | |
} | |
document.addEventListener('mousemove', onMouseMove); | |
document.addEventListener('mouseup', onMouseUp); | |
}); | |
} | |
// Create horizontal scrollbar if needed | |
if (element.scrollWidth > element.clientWidth) { | |
const scrollbar = document.createElement('div'); | |
scrollbar.className = 'virtual-scrollbar horizontal'; | |
scrollbar.style.width = `${element.clientWidth}px`; | |
const thumb = document.createElement('div'); | |
thumb.className = 'virtual-scrollbar-thumb horizontal'; | |
// Calculate thumb width | |
const thumbWidth = Math.max( | |
30, | |
element.clientWidth * (element.clientWidth / element.scrollWidth) | |
); | |
thumb.style.width = `${thumbWidth}px`; | |
scrollbar.appendChild(thumb); | |
element.appendChild(scrollbar); | |
// Show scrollbar on hover | |
element.addEventListener('mouseenter', () => { | |
scrollbar.style.opacity = '1'; | |
setTimeout(() => { | |
if (!scrollbar.classList.contains('active')) { | |
scrollbar.style.opacity = '0'; | |
} | |
}, 1500); | |
}); | |
// Update thumb position on scroll | |
element.addEventListener('scroll', () => { | |
const scrollRatio = element.scrollLeft / (element.scrollWidth - element.clientWidth); | |
const thumbLeft = scrollRatio * (element.clientWidth - thumbWidth); | |
thumb.style.left = `${thumbLeft}px`; | |
scrollbar.style.opacity = '1'; | |
clearTimeout(element.hScrollTimer); | |
element.hScrollTimer = setTimeout(() => { | |
if (!scrollbar.classList.contains('active')) { | |
scrollbar.style.opacity = '0'; | |
} | |
}, 1500); | |
}); | |
// Make thumb draggable | |
thumb.addEventListener('mousedown', (e) => { | |
e.preventDefault(); | |
scrollbar.classList.add('active'); | |
const startX = e.clientX; | |
const startLeft = thumb.offsetLeft; | |
const maxLeft = element.clientWidth - thumbWidth; | |
function onMouseMove(e) { | |
const deltaX = e.clientX - startX; | |
const newLeft = Math.max(0, Math.min(maxLeft, startLeft + deltaX)); | |
thumb.style.left = `${newLeft}px`; | |
const scrollRatio = newLeft / maxLeft; | |
element.scrollLeft = scrollRatio * (element.scrollWidth - element.clientWidth); | |
} | |
function onMouseUp() { | |
document.removeEventListener('mousemove', onMouseMove); | |
document.removeEventListener('mouseup', onMouseUp); | |
scrollbar.classList.remove('active'); | |
setTimeout(() => { | |
scrollbar.style.opacity = '0'; | |
}, 1500); | |
} | |
document.addEventListener('mousemove', onMouseMove); | |
document.addEventListener('mouseup', onMouseUp); | |
}); | |
} | |
} | |
// Initial run | |
addVirtualScrollbars(); | |
// Observe DOM changes to add scrollbars to new elements | |
const observer = new MutationObserver((mutations) => { | |
let shouldCheck = false; | |
for (const mutation of mutations) { | |
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { | |
shouldCheck = true; | |
break; | |
} | |
} | |
if (shouldCheck) { | |
addVirtualScrollbars(); | |
} | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
// Handle window resize | |
window.addEventListener('resize', () => { | |
// Remove existing scrollbars | |
document.querySelectorAll('.virtual-scrollbar').forEach(sb => sb.remove()); | |
// Reset all containers | |
document.querySelectorAll('.virtual-scrollbar-container').forEach(el => { | |
el.removeAttribute('data-has-virtual-scrollbar'); | |
}); | |
// Add new scrollbars | |
addVirtualScrollbars(); | |
}); | |
// Return observer to allow stopping if needed | |
return observer; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment