Sticky elements

How to create a sticky element that stops when it reaches another specified waypoint (element)

by hellosze

HTML

<script src="http://imakewebthings.com/jquery-waypoints/waypoints.js"></script>
<script src="http://imakewebthings.com/jquery-waypoints/shortcuts/sticky-elements/waypoints-sticky.js"></script>
<article>This element will be sticky (pos:fixed) when we scroll past it. When scrolling back up it will return as it was before.<br/>It will not scroll past the green footer. More on <a href="http://codesandnotes.com/sticky-elements/" target="_blank">Codes &amp; Notes: Sticky elements/</a></article>
<footer></footer>

CSS

body {
    /*extra space to test scrolling*/
    margin: 100px 0 1000px;
}

/* This is our sticky element*/
article {
    width: 60%;
    z-index: 2;
    background:pink;
}
/* This class gets applied by JavaScript */
.sticky {
    position: fixed;
    background: red;
    right: 0;
    top: 0;
}
/* The scripts also wraps the element in this to avoid jumpy behaviour when switching to pos: fixed */
.article-sticky-wrapper {}

/* This is our element that will stop the sticky element from scrolling */
footer {
    margin-top: 300px;
    height: 300px;
    width:100%;
    z-index: 1;
    position: relative;
    background:green;
}

JavaScript

var stickyElement = function () {
    // element to be sticky
    var $stickyEl = $("article");
    // element that will stop the sticky element
    var $stopEl = $('footer');

    $stickyEl.waypoint('sticky', {
        wrapper: '<div class="article-sticky-wrapper" />',
        stuckClass: 'sticky',
        offset: -1
    });

    $stopEl.waypoint(function (direction) {
        if (direction == 'down') {
            // when scrolling down
            // replace pos:fixed with absolute and set top value to
            // the distance from $stopEl to viewport top minus the 
            // height of the stickyElement 
            var footerOffset = $stopEl.offset();
            $stickyEl.css({
                position: 'absolute',
                top: footerOffset.top - $stickyEl.outerHeight()
            });
        } else if (direction == 'up') {
            // remove the inline styles so sticky styles apply again
            $stickyEl.attr('style', '');
        }

    }, {
        // trigger the waypoint when the bottom of stickyEl touches top of stopEl
        offset: function () {
            return $stickyEl.outerHeight();
        }
    });
};

stickyElement();