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;