Floating Scrollbar For Long Content - jQuery floating-scroll

Floating Scrollbar For Long Content - jQuery floating-scroll
File Size: 25.9 KB
Views Total:
Last Update:
Publish Date:
Official Website: Go to website
License: MIT
   

floating-scroll is a jQuery plugin which appends a floating horizontal scrollbar to the bottom of the webpage so the long content (e.g. large table with tons of columns) will always be scrollable as you scroll down.

Install it via package managers:

# Yarn
$ yarn add floating-scroll

# NPM
$ npm install floating-scroll --save

How to use it:

1. The jQuery floating-scroll plugin requires jQuery library to work properly.

<script src="//code.jquery.com/jquery.min.js"></script>

2. Import the jQuery floating-scroll plugin plugin's JavaScript and CSS files into the html file.

<link href="jquery.floatingscroll.css" rel="stylesheet">
<script src="src/jquery.floatingscroll.js"></script>

3. Enable the floating-scroll plugin on the target scrollable content.

<pre>
/*!
 * jQuery floatingScroll Plugin v2.3.3
 * https://github.com/Amphiluke/floating-scroll
 * (c) 2017 Amphiluke
 */
(function (global, factory) {
    "use strict";
    if (typeof define === "function" && define.amd) {
        define(["jquery"], factory);
    } else if (typeof module === "object" && module.exports) {
        factory(require("jquery"));
    } else {
        factory(global.jQuery);
    }
}(this, function ($) {
    "use strict";

    function FScroll(cont) {
        var inst = this,
            scrollBody = cont.closest(".fl-scrolls-body");
        inst.cont = cont[0];
        if (scrollBody.length) {
            inst.scrollBody = scrollBody;
        }
        inst.sbar = inst.initScroll();
        inst.visible = true;
        inst.updateAPI(); // recalculate floating scrolls and hide those of them whose containers are out of sight
        inst.syncSbar(inst.cont);
        inst.addEventHandlers();
    }

    $.extend(FScroll.prototype, {
        initScroll: function () {
            var flscroll = $("<div class='fl-scrolls'></div>");
            $("<div></div>").appendTo(flscroll).css({width: this.cont.scrollWidth + "px"});
            return flscroll.appendTo(this.cont);
        },

        addEventHandlers: function () {
            var inst = this,
                handlers,
                i, len;
            handlers = inst.eventHandlers = [
                {
                    $el: inst.scrollBody || $(window),
                    handlers: {
                        // Don't use `$.proxy()` since it makes impossible event unbinding individually per instance
                        // (see the warning at http://api.jquery.com/unbind/)
                        scroll: function () {
                            inst.checkVisibility();
                        },
                        resize: function () {
                            inst.updateAPI();
                        }
                    }
                },
                {
                    $el: inst.sbar,
                    handlers: {
                        scroll: function () {
                            inst.visible && inst.syncCont(this, true);
                        }
                    }
                },
                {
                    $el: $(inst.cont),
                    handlers: {
                        scroll: function () {
                            inst.syncSbar(this, true);
                        },
                        focusin: function () {
                            setTimeout(function () {
                                inst.syncSbar(inst.cont);
                            }, 0);
                        },
                        // The `adjustScroll` event type is kept for backward compatibility only.
                        "update.fscroll adjustScroll": function (e) {
                            // Check event namespace to ensure that this is not an extraneous event in a bubbling phase
                            if (e.namespace === "fscroll" || e.type === "adjustScroll") {
                                inst.updateAPI();
                            }
                        },
                        "destroy.fscroll": function (e) {
                            if (e.namespace === "fscroll") {
                                inst.destroyAPI();
                            }
                        }
                    }
                }
            ];
            for (i = 0, len = handlers.length; i < len; i++) {
                handlers[i].$el.bind(handlers[i].handlers);
            }
        },

        checkVisibility: function () {
            var inst = this,
                mustHide = (inst.sbar[0].scrollWidth <= inst.sbar[0].offsetWidth),
                contRect,
                maxVisibleY;
            if (!mustHide) {
                contRect = inst.cont.getBoundingClientRect();
                maxVisibleY = inst.scrollBody ?
                    inst.scrollBody[0].getBoundingClientRect().bottom :
                    window.innerHeight || document.documentElement.clientHeight;
                mustHide = ((contRect.bottom <= maxVisibleY) || (contRect.top > maxVisibleY));
            }
            if (inst.visible === mustHide) {
                inst.visible = !mustHide;
                // we cannot simply hide a floating scroll bar since its scrollLeft property will not update in that case
                inst.sbar.toggleClass("fl-scrolls-hidden");
            }
        },

        syncCont: function (sender, preventSyncSbar) {
            // Prevents next syncSbar function from changing scroll position
            if (this.preventSyncCont === true) {
                this.preventSyncCont = false;
                return;
            }
            this.preventSyncSbar = !!preventSyncSbar;
            this.cont.scrollLeft = sender.scrollLeft;
        },

        syncSbar: function (sender, preventSyncCont) {
            // Prevents next syncCont function from changing scroll position
            if (this.preventSyncSbar === true) {
                this.preventSyncSbar = false;
                return;
            }
            this.preventSyncCont = !!preventSyncCont;
            this.sbar[0].scrollLeft = sender.scrollLeft;
        },

        // Recalculate scroll width and container boundaries
        updateAPI: function () {
            var inst = this,
                cont = inst.cont,
                pos = cont.getBoundingClientRect();
            inst.sbar.width($(cont).outerWidth());
            if (!inst.scrollBody) {
                inst.sbar.css("left", pos.left + "px");
            }
            $("div", inst.sbar).width(cont.scrollWidth);
            inst.checkVisibility(); // fixes issue #2
        },

        // Remove a scrollbar and all related event handlers
        destroyAPI: function () {
            var handlers = this.eventHandlers,
                i, len;
            for (i = 0, len = handlers.length; i < len; i++) {
                handlers[i].$el.unbind(handlers[i].handlers);
            }
            this.sbar.remove();
        }
    });

    // `attachScroll` is the old alias used in v1.X. Temporally kept for backward compatibility.
    $.fn.attachScroll = $.fn.floatingScroll = function (method) {
        if (!arguments.length || method === "init") {
            this.each(function () {
                new FScroll($(this));
            });
        } else if (FScroll.prototype.hasOwnProperty(method + "API")) {
            this.trigger(method + ".fscroll");
        }
        return this;
    };
}));
</pre>
$("pre").floatingScroll();

4. API methods.

// update the progress bar
$(".element").floatingScroll("update");

// destroy the progress bar
$(".element").floatingScroll("destroy");

This awesome jQuery plugin is developed by Amphiluke. For more Advanced Usages, please check the demo page or visit the official website.