Skip to content

Instantly share code, notes, and snippets.

@GeekBoySupreme
Created May 19, 2025 08:59
Show Gist options
  • Save GeekBoySupreme/4ef74bc335007359c6a14aecbf45aaa6 to your computer and use it in GitHub Desktop.
Save GeekBoySupreme/4ef74bc335007359c6a14aecbf45aaa6 to your computer and use it in GitHub Desktop.
To eliminate scrollbars from a page
(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