Make fixed sidebar scroll 290px from bottom of page?

J82 picture J82 · Mar 21, 2014 · Viewed 11.7k times · Source

I am trying to create a sidebar that functions like the one on Vice.com. If you scroll down, the sidebar will become fixed at a certain point, and then when the sidebar reaches a certain point near the bottom of the site, it will then continue to scroll up with the rest of the site.

On my site, I am stuck on the second part which is getting the sidebar to continue scrolling up once it hits near the bottom. 290px from the bottom to be exact.

Here is what I have so far:

JavaScript

<script>
jQuery(window).scroll(function () {
    var threshold = 20;
 if (jQuery(window).scrollTop() >= 20)
     jQuery('#sidebar').addClass('fixed');
 else
     jQuery('#sidebar').removeClass('fixed');
});
</script>

CSS

#sidebar {
  margin: 0;
  position: absolute;
  right: 0;
  width: 220px;
}
#sidebar.fixed {
  margin-left: 720px;
  position:fixed;
  right: auto;
  top: 173px;
}

How can I make the fixed sidebar scroll up once it hits a certain point near the bottom?


Edit #1

Here is the updated code by Adam. I'm using a conditional statement for the pages that have a different threshold. Nothing happens when I use this code, meaning the sidebar doesn't get the fixed class added and therefore scrolls up normally as if the code isn't even there. Also, I get a console error on this line if (scrollTop() >= 236) { saying that the "number is not a function".

if (jQuery(document.body).hasClass("home")) {

    jQuery(window).scroll(function () {
        var sidebarHeight = jQuery('#sidebar').height(),
            containerHeight = jQuery('#container').height() + 173,
            scrollTop = jQuery(window).scrollTop(),
            clientHeight = scrollTop + jQuery(window).height(),
            threshold = 654;
        if (scrollTop() >= 654) {
            jQuery('#sidebar').addClass('fixed');
        } else if (containerHeight - scrollTop <= sidebarHeight) {
            jQuery('#sidebar').removeClass('fixed').addClass('bottom');
        }
    });

} else if (jQuery(document.body).hasClass("single") || jQuery(document.body).hasClass("page")) {

    jQuery(window).scroll(function () {
        var sidebarHeight = jQuery('#sidebar').height(),
            containerHeight = jQuery('#container').height() + 173,
            scrollTop = jQuery(window).scrollTop(),
            clientHeight = scrollTop + jQuery(window).height(),
            threshold = 20;
        if (scrollTop() >= 20) {
            jQuery('#sidebar').addClass('fixed');
        } else if (containerHeight - scrollTop <= sidebarHeight) {
            jQuery('#sidebar').removeClass('fixed').addClass('bottom');
        }
    });

} else {

    jQuery(window).scroll(function () {
        var sidebarHeight = jQuery('#sidebar').height(),
            containerHeight = jQuery('#container').height() + 173,
            scrollTop = jQuery(window).scrollTop(),
            clientHeight = scrollTop + jQuery(window).height(),
            threshold = 236;
        if (scrollTop() >= 236) {
            jQuery('#sidebar').addClass('fixed');
        } else if (containerHeight - scrollTop <= sidebarHeight) {
            jQuery('#sidebar').removeClass('fixed').addClass('bottom');
        }
    });

}

Below is the HTML structure as requested:

<!-- BEGIN #masthead-->
<div id="masthead">
    <!-- #secondary-menu -->
    <div id="secondary-menu">

        <!-- .centered-menu -->
        <div class="centered-menu">
            <div class="latest-tweets"></div>
            <div id="search-bar"></div>
            <ul class="social-icons sf-js-enabled"></ul>
        </div>
        <!-- /.centered-menu -->

    </div>
    <!-- /#secondary-menu -->

    <!-- BEGIN #header-->
    <div id="header">

        <!-- #header-inner -->
        <div id="header-inner" class="clearfix">
            <div id="logo"></div>

            <!-- BEGIN #primary-menu -->
            <div id="primary-menu" class="clearfix">

                <!-- .left-menu -->
                <div class="left-menu split-menu"></div>
                <!-- /.left-menu -->

                <!-- .right-menu -->
                <div class="right-menu split-menu">
                    <div class="menu-menu-right-container"></div>
                <!-- /.right-menu -->   

            <!-- END #primary-menu -->
            </div>

        </div>
        <!-- /#header-inner -->

    <!-- END #header -->

    <!-- BEGIN #mobile-menu -->
    <div id="mobile-menu">
        <div id="mobile-inner"></div>
    </div>
    <!-- END #mobile-menu -->

    </div>

    <div id="categories-bar"></div>

</div>
<div id="masthead-space"></div>
<!-- END #masthead -->

<!-- BEGIN #wrapper-->
<div id="wrapper">

    <!-- BEGIN #page-->
    <div id="page">

        <div id="main" class="clearfix">

            <div id="container" class="clearfix">

                <!--BEGIN #content -->
                <div id="content">
                    <div id="sidebar"></div><!-- #sidebar --> 
                </div>

            </div>

        <!--END #main -->
        </div>

    <!--END #page -->
    </div>

<!--END #wrapper -->
</div>

<!--BEGIN #bottom -->
<div id="bottom">

    <!--BEGIN #footer -->
    <div id="footer"></div>

</div>

Answer

Aramil Rey picture Aramil Rey · Apr 20, 2015

My advise:

Create 2 CSS classes with the characteristics you want and toggle both after reaching the break point, 1 must be active at the begining.

FIDDLE

JS

var changePoint = $('#reaching_the_top_of_this_element_activates_change').offset().top;

$(window).scroll(function ()  {
if ($(window).scrollTop() >= changePoint) {
    $('#sidebar').removeClass('blackStatic');
    $('#sidebar').addClass('redFixed');
}
else {
    $('#sidebar').addClass('blackStatic');
    $('#sidebar').removeClass('redFixed');
}

});

CSS

.blackStatic {
    position: static;
    background-color: black;
    color: red;
}
.redFixed {
    position: fixed;
    top: 0;
    background-color: red;
    color: black;
}