Sticky scrollbar at bottom of table

2019-04-19 00:19发布

问题:

I'm not sure if "sticky" is the term for this, but is there a way to make the scrollbar from overflow:auto stay visible?

I have a rather large table that I want to be scrollable horizontally; however, the table is fairly tall as well, so when the page loads the horizontal scrollbar is not within the viewport of the browser, so it's rather hard to tell that the table is scrollable at all.

<div style = 'width:900px;overflow:auto'>
    <table>
        <!-- Very large table here -->
    </table>
</div>

The scroll bar appears below the table, but unfortunately the table is so tall you can't see it unless you scroll down.

I'd like to have the horizontal scrollbar stay visible even if the table goes off the screen, maybe fixed to the bottom of the viewport. Ideally I'd like to do it using only CSS or a minimal amount of javascript.

回答1:

Here is a script for that http://jsfiddle.net/TBnqw/2288/

$(function($){
    var scrollbar = $('<div id="fixed-scrollbar"><div></div></div>').appendTo($(document.body));
    scrollbar.hide().css({
        overflowX:'auto',
        position:'fixed',
        width:'100%',
        bottom:0
    });
    var fakecontent = scrollbar.find('div');

    function top(e) {
        return e.offset().top;
    }

    function bottom(e) {
        return e.offset().top + e.height();
    }

    var active = $([]);
    function find_active() {
        scrollbar.show();
        var active = $([]);
        $('.fixed-scrollbar').each(function() {
            if (top($(this)) < top(scrollbar) && bottom($(this)) > bottom(scrollbar)) {
                fakecontent.width($(this).get(0).scrollWidth);
                fakecontent.height(1);
                active = $(this);
            }
        });
        fit(active);
        return active;
    }

    function fit(active) {
        if (!active.length) return scrollbar.hide();
        scrollbar.css({left: active.offset().left, width:active.width()});
        fakecontent.width($(this).get(0).scrollWidth);
        fakecontent.height(1);
        delete lastScroll;
    }

    function onscroll(){
        var oldactive = active;
        active = find_active();
        if (oldactive.not(active).length) {
            oldactive.unbind('scroll', update);
        }
        if (active.not(oldactive).length) {
            active.scroll(update);
        }
        update();
    }

    var lastScroll;
    function scroll() {
        if (!active.length) return;
        if (scrollbar.scrollLeft() === lastScroll) return;
        lastScroll = scrollbar.scrollLeft();
        active.scrollLeft(lastScroll);
    }

    function update() {
        if (!active.length) return;
        if (active.scrollLeft() === lastScroll) return;
        lastScroll = active.scrollLeft();
        scrollbar.scrollLeft(lastScroll);
    }

    scrollbar.scroll(scroll);

    onscroll();
    $(window).scroll(onscroll);
    $(window).resize(onscroll);
});

It is a quick test rather than a complete generic plugin, but is a good start, I think



回答2:

Here's my take, @user2451227's is almost perfect, but didn't work with nested overflowed elements and had a number of performance issues, so I rewrote it:

$(function($){
    var fixedBarTemplate = '<div class="fixed-scrollbar"><div></div></div>';
    var fixedBarCSS = { display: 'none', overflowX: 'scroll', position: 'fixed',  width: '100%', bottom: 0 };

    $('.fixed-scrollbar-container').each(function() {
        var $container = $(this);
        var $bar = $(fixedBarTemplate).appendTo($container).css(fixedBarCSS);

        $bar.scroll(function() {
            $container.scrollLeft($bar.scrollLeft());
        });

        $bar.data("status", "off");
    });

    var fixSize = function() {
        $('.fixed-scrollbar').each(function() {
            var $bar = $(this);
            var $container = $bar.parent();

            $bar.children('div').height(1).width($container[0].scrollWidth);
            $bar.width($container.width()).scrollLeft($container.scrollLeft());
        });

        $(window).trigger("scroll.fixedbar");
    };

    $(window).on("load.fixedbar resize.fixedbar", function() {
        fixSize();
    });

    var scrollTimeout = null;

    $(window).on("scroll.fixedbar", function() { 
        clearTimeout(scrollTimeout);
        scrollTimeout = setTimeout(function() {
            $('.fixed-scrollbar-container').each(function() {
                var $container = $(this);
                var $bar = $container.children('.fixed-scrollbar');

                if($bar.length && ($container[0].scrollWidth > $container.width())) {
                    var containerOffset = {top: $container.offset().top, bottom: $container.offset().top + $container.height() };
                    var windowOffset = {top: $(window).scrollTop(), bottom: $(window).scrollTop() + $(window).height() };

                    if((containerOffset.top > windowOffset.bottom) || (windowOffset.bottom > containerOffset.bottom)) {
                        if($bar.data("status") == "on") {
                            $bar.hide().data("status", "off");
                        }
                    } else {
                        if($bar.data("status") == "off") {
                            $bar.show().data("status", "on");
                            $bar.scrollLeft($container.scrollLeft());
                        }
                    }
                } else {
                    if($bar.data("status") == "on") {
                        $bar.hide().data("status", "off");
                    }
                }
            });
        }, 50);
    });

    $(window).trigger("scroll.fixedbar");
});

Usage: Add the class fixed-scrollbar-container to your horizontally overflowed element, then include this code. If the container is updated or changes in size, run $(window).trigger("resize.fixedbar"); to update the bar.

Demo: http://jsfiddle.net/8zoks7wz/1/



回答3:

How about restricting the height of the containing div so it stays within the body? You could then have the table scroll within that div.

Working jsfiddle here: http://jsfiddle.net/fybLK/

html, body {height: 100%; margin: 0; padding: 0;}
div {
    width:500px;
    max-height: 100%;
    overflow:auto;
    background: steelblue;}
table {
    width: 1000px;
    height: 1000px;
    color: #fff;}

Here, I've set the html and body to 100% height so that the containing div can be sized.



回答4:

@Mahn - I made a small update to the following function:

$('.fixed-scrollbar-container').each(function() {

    var container = jQuery(this);

    if (container[0].offsetWidth < container[0].scrollWidth) {
        var bar = jQuery(fixedBarTemplate).appendTo(container).css(fixedBarCSS);

        bar.scroll(function() {
            container.scrollLeft(bar.scrollLeft());
        });

        bar.data("status", "off");    
    }
});

The if statement looks if the container offsetWidth is smaller than the scrollWidth. Else you will also get a fixed scrollbar if the content happens to be smaller than the container. I did not like having a disfunctional scrollbar, hence this edit.