<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>
Created
February 20, 2024 17:21
-
-
Save xTCry/fea4889c7f26ac650f0a375b6fb55b58 to your computer and use it in GitHub Desktop.
[JS] Sticky Scrollbar to bottom for table
This file contains 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 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