Skip to content

Instantly share code, notes, and snippets.

@xTCry
Created February 20, 2024 17:21
Show Gist options
  • Save xTCry/fea4889c7f26ac650f0a375b6fb55b58 to your computer and use it in GitHub Desktop.
Save xTCry/fea4889c7f26ac650f0a375b6fb55b58 to your computer and use it in GitHub Desktop.
[JS] Sticky Scrollbar to bottom for table

Sticky Scrollbar to bottom for table

<div class="table-responsive">
    <table class="table">
        <thead class="table-primary">
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>Test 1</td>
            </tr>
            <tr>
                <td>2</td>
                <td>Test 2</td>
            </tr>
        </tbody>
    </table>
</div>

<script>
// import './xtt.scroller-sticky.js';
$(() => {
    xtt_scrollerSticky('.table-responsive');
});
</script>
function xtt_scrollerSticky(selectors = '') {
if (!selectors) return;
if (!this.styleInited) {
const style = document.createElement('style');
style.innerHTML = `
.xtt-scroller {
position: fixed;
left: 0;
right: 0;
bottom: 0;
overflow-x: scroll;
display: none;
}
`;
document.head.appendChild(style);
this.styleInited = true;
}
const $containers = document.querySelectorAll(selectors);
for (const $container of $containers) {
if ($container.getElementsByClassName('xtt-scroller').length > 0) {
break;
}
const $scroller = document.createElement('div');
$scroller.className = 'xtt-scroller';
$container.appendChild($scroller);
const $scrollerContent = document.createElement('div');
$scrollerContent.style.height = '1px';
$scroller.appendChild($scrollerContent);
let needUpdate = true;
const scrollbarPositioner = () => {
const scrollTop = document.scrollingElement.scrollTop;
const wrapperTop = $container.offsetTop;
const wrapperBottom = wrapperTop + $container.offsetHeight;
const topMatch = window.innerHeight + scrollTop >= wrapperTop;
const bottomMatch = scrollTop <= wrapperBottom;
const hasHorizontalScrollbar = $container.scrollWidth > $container.clientWidth;
const hasVerticalScrollbar = $container.scrollHeight > $container.clientHeight;
if (topMatch && bottomMatch && hasHorizontalScrollbar) {
const inside =
wrapperBottom >= scrollTop &&
window.innerHeight + scrollTop <= wrapperBottom;
if (inside) {
// $scroller.style.bottom = "0px";
$scroller.style.display = 'block';
if (needUpdate) {
$scroller.scrollLeft = $container.scrollLeft;
needUpdate = false;
}
} else {
needUpdate = true;
// const offset = scrollTop + window.innerHeight - wrapperBottom;
// $scroller.style.bottom = offset + "px";
$scroller.style.display = 'none';
}
const pos = $container.getBoundingClientRect();
$scroller.style.width = ($container.offsetWidth - (hasVerticalScrollbar ? 17 : 0)) + "px";
$scroller.style.left = (pos.left /* + (pos.right - pos.width) */) + "px";
$scrollerContent.style.width = ($container.scrollWidth) + "px";
} else {
$scroller.style.display = 'none';
}
requestAnimationFrame(scrollbarPositioner);
};
requestAnimationFrame(scrollbarPositioner);
// Sync scrolling
let ignoreScrollEvent = false;
let animation = null;
$scroller.addEventListener("scroll", (e) => {
if (ignoreScrollEvent) return false;
if (animation) cancelAnimationFrame(animation);
animation = requestAnimationFrame(() => {
ignoreScrollEvent = true;
$container.scrollLeft = $scroller.scrollLeft;
ignoreScrollEvent = false;
});
});
$container.addEventListener("scroll", (e) => {
if (ignoreScrollEvent) return false;
if (animation) cancelAnimationFrame(animation);
animation = requestAnimationFrame(() => {
ignoreScrollEvent = true;
$scroller.scrollLeft = $container.scrollLeft;
ignoreScrollEvent = false;
});
});
}
}
xtt_scrollerSticky.prototype.styleInited = false;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment