Search code examples
cssvue.jsvuetify.js

Sticky header on scroll in vuetify datatable


When vuetify datatable height is greater than the window height, when we scroll the page, I want the header row sticky till the datatable scroll is over

The behaviour should be similar to this https://output.jsbin.com/zuzuqe/1/

also like the datatable they used https://www.worldometers.info/coronavirus/

I've also tried

th {
    position:sticky;  
    top: 0;
    background-color: white;
}

The position sticky is relative to the datatable and not to the window

Can anyone suggest me some idea or codepen on how to implement the same using Vuetify datatable


Solution

  • You can do that with a Vue Component Float Thead vue component

    EDIT : This is vue directive usable with vuetify v-simple-table

    Use :

    <v-simple-table v-simple-table-sticky></v-simple-table>
    

    Directive :

    function stickyScrollHandler(el) {
        return () => {
            const getOffsetTop = function(element) {
                let offsetTop = 0;
                while (element) {
                    offsetTop += element.offsetTop;
                    element = element.offsetParent;
                }
                return offsetTop;
            }
    
            const table = el.querySelector("table");
            const tableHeader = el.querySelector(".adx-table_sticky_header");
            let tableHeaderFloat = el.querySelector(".adx-table_sticky--float");
    
            const pos = getOffsetTop(table) - window.scrollY;
    
            if (pos < 0) {
                if (!tableHeaderFloat) {
                    const clone = tableHeader.cloneNode(true);
                    clone.classList.remove('.table_sticky_header');
    
                    tableHeaderFloat = document.createElement('table');
                    tableHeaderFloat.appendChild(clone);
                    tableHeaderFloat.classList.add("adx-table_sticky--float");
                    table.parentNode.appendChild(tableHeaderFloat);
    
                    tableHeader.style.opacity = 0;
                }
    
                if (Math.abs(pos) < table.offsetHeight - tableHeaderFloat.offsetHeight) {
                    tableHeaderFloat.style.position = "absolute";
                    tableHeaderFloat.style.top = Math.abs(pos) + "px";
                }
            } else {
                if (tableHeaderFloat) {
                    tableHeaderFloat.remove();
                }
    
                tableHeader.style.opacity = 1;
            }
        }
    }
    
    Vue.directive("simple-table-sticky", {
        bind: function(el, binding, vnode) {
            el.querySelector("table thead").classList.add("adx-table_sticky_header");
            el.style.position = "relative"
    
            window.addEventListener('scroll', stickyScrollHandler(el));
        },
        unbind: function(el) {
            window.removeEventListener('scroll', stickyScrollHandler(el));
        }
    });