Skip to content

Instantly share code, notes, and snippets.

@xTCry
Created February 20, 2024 17:21
Show Gist options
  • Select an option

  • Save xTCry/fea4889c7f26ac650f0a375b6fb55b58 to your computer and use it in GitHub Desktop.

Select an option

Save xTCry/fea4889c7f26ac650f0a375b6fb55b58 to your computer and use it in GitHub Desktop.

Revisions

  1. xTCry created this gist Feb 20, 2024.
    31 changes: 31 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    # Sticky Scrollbar to bottom for table

    ```html
    <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>
    ```
    102 changes: 102 additions & 0 deletions xtt.scroller-sticky.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    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;