Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Free-Scrolling Sticky Sidebar Without jQuery

I'm trying to achieve what is outlined in this Stack Overflow question, without jQuery dependency: stackoverflow.com/questions/18358816/sticky-sidebar-stick-to-bottom-when-scrolling-down-top-when-scrolling-up

But I didn't want to hijack that question.

Basically, I want the content in the sidebar to be independently scrollable but fixed when the viewport reaches either end of the sidebars contents on scroll.

My main stumbling block appears to not being able to calculate the elementTop variable when the sidebar is absolutely positioned and between the top and bottom of the container which I have set to be full height.

Full code below:

var StickySidebar = function(eventie) {

var container, containerTop, containerHeight,  // container
element, elementTop, elementHeight, elStyle,  // element
viewportTop = -1, viewportHeight, documentTop, // viewport
lastViewportTop, scrollingDown, top = false , bottom = false,// sticky vars

scroll = window.requestAnimationFrame ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame ||
         window.msRequestAnimationFrame ||
         window.oRequestAnimationFrame ||
         function(callback){ window.setTimeout(callback, 1000/60); },

options =  {
    container : document.querySelector('.sidebar-container'),
    element : document.querySelector('.sidebar'),
    sidebarClass : 'sidebar',
    bottomOffset : -15,
    topOffset: 90,
},

_updateValue = function() {
    viewportHeight = window.innerHeight;
},

_offset = function(obj) {    
    var ol = ot = 0;
    if (obj.offsetParent) {
        do {
            ol += obj.offsetLeft;
            ot += obj.offsetTop;
        } while (obj = obj.offsetParent);
    }
    return {
        left: ol,
        top: ot
    };
},


init = function(){
    if(options.element !== null) {

        container = options.container;
        containerTop = offset(container).top;
        containerHeight = container.clientHeight;

        element = options.element;
        elementTop = offset(element).top;
        elementHeight = options.element.clientHeight;

        lastViewportTop = window.scrollY;
        viewportHeight = window.innerHeight;

        eventie.bind(document, "scroll", _loop);
        eventie.bind(window, "resize", _updateValue);

    }
},

_loop = function() {
    if (viewportTop == window.pageYOffset) {
        scroll(_loop);
        return false;
    } else viewportTop = window.pageYOffset;

    _updateValue();

    var viewportBottom, elementTooBig, topOffset;

    elementTop = offset(element).top;

    elementHeight = element.clientHeight;
    containerHeight = container.clientHeight;
    scrollingDown = viewportTop > lastViewportTop;
    elementTooBig = elementHeight > viewportHeight;

    console.log("elementTop : " + elementTop);
    console.log("viewportTop : " + viewportTop);

    if (scrollingDown) {

        if (viewportTop + viewportHeight >= elementTop + elementHeight) {
            element.setAttribute('style','position:fixed; bottom:30px;');
        } else {
            element.setAttribute('style','position:absolute; top:'+ elementTop +'px;'); // issue 1
    }

    if (viewportTop + viewportHeight > containerTop + containerHeight) {
        element.setAttribute('style','position:absolute; bottom:0;');
    }

} else {

    if (viewportTop < containerTop - 60) {
        element.removeAttribute('style');
        return;
    }

    if (viewportTop <= elementTop) {
        element.setAttribute('style','position:fixed; top:90px;');
    } else {
        element.setAttribute('style','position:absolute; top:'+ elementTop +'px;');
        elementTop = viewportTop + elementTop;
    }

}

lastViewportTop = viewportTop;

};


return {
    init: init
};

}(eventie);

I've been trying to tackle this issue for a few weeks now, and it has been driving me insane. Any help would be greatly appreciated.

like image 393
Seán O'Grady Avatar asked Dec 11 '15 00:12

Seán O'Grady


1 Answers

If you're just trying to achieve the desired result not necessarily creating that yourself - well, there are many JavaScript libraries that provide that.

For example, Stickyfill is actually a polyfill for position: sticky which is natively supported only in Firefox 41+ and Safari 8+. Here is the demo with all kinds of stickiness you can imagine :)

P.S. At first glance you might notice something about jQuery there, but it's pure JavaScript and just adds a jQuery extension.

like image 166
Alexander Mikhalchenko Avatar answered Oct 21 '22 15:10

Alexander Mikhalchenko