Make fixed sidebar scroll 290px from bottom of page?
Asked Answered
P

9

11

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>
Productive answered 21/3, 2014 at 2:24 Comment(1)
I'm not positive on the structure of your page so my answer my be a bit off but I'll try to make it work. If you can make me a fiddle I could me more accurate.Linguistician
A
1

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;
}
Asphyxiant answered 20/4, 2015 at 15:51 Comment(0)
L
0

As I said in my comment, this may be off because I can't see the structure of the html right now. But something like this should work. We get the height of the scrollbar, container(+ the height of the header), and the height of the viewable area for the user or the client. So using maths, we can say that when the container - scrollTop is less than the height of the scrollbar we need the scrollbar to stop being fixed. At this point we remove the fixed class and add the bottom class that just has 1 property. bottom: 0. Now as long as the scrollbar is inside of the container and the container has position: relative it's bottom will be fixed to the bottom of the container.

Javascript:

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');
    }
});

CSS:

#sidebar.bottom {
    bottom: 0;
}

Let me know if this doesn't work for you, and update your question to have the html so this can be made more to your needs. Also you will have to deal with a user scrolling back up the page too, which this does not account for currently but it wouldn't be hard to add.

Linguistician answered 21/3, 2014 at 3:24 Comment(0)
F
0

take a look at bootstrap-affix.js http://getbootstrap.com/2.3.2/javascript.html#affix

It does exactly what you are asking for, and you can use html5 data-attributes to add the behaviors:

<div data-spy="affix" data-offset-top="200">...</div>
Fustian answered 21/3, 2014 at 3:41 Comment(2)
I've looked into this and I can't figure it out. I don't know much about bootstrap, unfortunately, but this does look like it does what I'm asking for.Productive
Sorry that I can't improve the answer for now, I'm on my mobile, the link is for an older version of bootstrap, for the latest with instructions, see this getbootstrap.com/javascriptFustian
E
0

try this code (I used ID's according to your markup):

function sidebarScroll()
{
  var tmpWindow = $(window),
      wrapper = $('#wrapper').height(),
      header = $('#header').height(),
      sidebar = $('#sidebar'),
      offsetTop = sidebar.offset().top,
      offsetBottom;

  tmpWindow.scroll(function(){

    offsetBottom = (wrapper + header) - sidebar.height();

    if (tmpWindow.scrollTop() < offsetTop) {
      sidebar.removeClass('fixed bottom');
    } else if (tmpWindow.scrollTop() > offsetBottom) {
      sidebar.removeClass('fixed').addClass('bottom');
    } else {
      sidebar.removeClass('bottom').addClass('fixed');
    }
  });
}

sidebarScroll();

and this is the classes you need:

#wrapper {
  position: relative;
}
#sidebar {
  width: 220px;
  position: absolute;
  right: 0;
}

#sidebar.fixed {
  position: fixed;
  top: 0;
}

#sidebar.bottom {
  position: absolute;
  bottom: 0;
}

DEMO

Ensemble answered 21/3, 2014 at 13:24 Comment(0)
L
0

The code you borrowed from Adam was copied wrong. You use scrollTop() instead of scrollTop. scrollTop is set to a number, so essentially, you're trying to do 20() (or whatever number scrollTop is set to)

Edit: In order to reduce redundancy with your code, I rewrote it just to have the threshold set via ternary if statements:

jQuery(window).scroll(function () {
    var sidebarHeight = jQuery('#sidebar').height(),
    containerHeight = jQuery('#container').height() + 173,
    scrollTop = jQuery(window).scrollTop(),
    clientHeight = scrollTop + jQuery(window).height(),
    threshold = (jQuery(document.body).hasClass("home"))?654:((jQuery(document.body).hasClass("single") || jQuery(document.body).hasClass("page"))?20:236);
});
Lunik answered 9/8, 2014 at 18:53 Comment(0)
H
0
<div class="main">
</div>
<div class="foot">
</div>
<div id="float">
<div class="float_content_head">
I was sending javascript to display
</div><div class="float_content">
d my issue was that I was sending javascript to display console messages. Even when I looked at the file in my browser (not through the application) it showed exactly as I expected it to (eg the extra tags weren't showing), but there were showing in the html/text output and were trying to be parsed. Hope this helps someone!I had a similar problem, and my issue was that I was sending tags we
</div>
</div>

Css
<style type="text/css">
#float{
    background: #fff;
    position:absolute;
    right:30px;
    top:20px;
    width:250px;
    padding:10px;
    border-radius: 6px;
    -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.43);
-moz-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.43);
box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.43);
}
.float_content_head{
    padding:10px;
    border-bottom: 1px solid #efefef;
    text-align:center;
}
.float_content{
    padding-top:10px;
}
.main{
    width: 800px;
    height: 800px;
    margin: 0 auto;
    border:1px solid #efefef;
    padding: 10px;
    background:#ccc;
}
.foot{
    width: 100%;
    height: 800px;
    margin: 0 auto;
    border:1px solid #efefef;
    padding: 10px;
    background:#096;
}
#box p{
    margin:0;
    padding:0;
}
</style>

JS
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript"> 
$(document).ready(function() {
var starting_position = $('#float').offset();
var top_padding = 50;
var bottom_limit = $('.foot').offset();
var box_height = $('#float').height() + 20;
$(window).scroll(function(){
var top_window = $(window).scrollTop();
if (top_window > starting_position.top && top_window < bottom_limit.top - box_height){
$('#float').stop().animate({top: top_window - starting_position.top + top_padding}, 400);
} else if (top_window > bottom_limit.top - starting_position.top - box_height){
$('#float').stop().animate({top: bottom_limit.top - starting_position.top - box_height }, 400);
} else { $('#float').stop().animate({top: 10 }, 400);           
}
});
});
</script>
Hawfinch answered 8/10, 2014 at 18:35 Comment(1)
Please add an explanation to the code to help the OP (and anyone who has the same problem) understand your solution.Allyson
J
0

It sounds like this is what you're after (excuse my IDs):

$(document).ready(function() {
// Get the inital position of the menu
    var initialPos = $('#mainnav').offset().top;

//Bind scrolling or resizing
$(window).bind('scroll resize', function() {

// Get the distance from the top of the page to the top of the footer
    var footerTop = $('#footer').length > 0 ? $('#footer').offset().top : $('#footer').offset().top;

// Get the distance from the top of the page to the bottom of the menu
    var navBottom = $(window).scrollTop() + $('#mainnav').height() + 22;

// If the user scrolls further than the height of the header (this method allows for resizing with a max-width layout)
    if ($(window).scrollTop() > initialPos) {
        $('#mainnav').css({
            'position': 'fixed',

           // move the menu based on how far the user has scrolled, and if it exceed the height of the footer
            'top': (footerTop - navBottom > 0 ? 0 : footerTop - navBottom) + 'px',
            'left': ($('#wrapper02').offset().left - $(window).scrollLeft()) + 'px'
        });
    } else {
        $('#mainnav').css({
            'position': 'static'
        });
    }
});
});
Juju answered 20/3, 2015 at 3:3 Comment(0)
W
0
    <style>

     #wrap {
     width:100%;
     height:1000px;
 }  
 #sidebar {
    width:100px;
    height:100px;
    float:left;
    background-color:black;
    color:white;
    vertical-align:middle;
    text-align:center;
 }
    </style>

    <script>
     var offset = $("#sidebar").offset();
 var topPadding = 15;

 $(window).scroll(function () {
    if ($(window).scrollTop() > offset.top) {
        $("#sidebar").stop().animate({
            marginTop: $(window).scrollTop() - offset.top + topPadding
        });
    } else {
        $("#sidebar").stop().animate({
            marginTop: 0
        });
    }
 });

    </script>

    <div id='wrap'>
        <div id="sidebar">Side Bar</div>
    </div>
Wardrobe answered 10/4, 2015 at 12:20 Comment(2)
Could you please elaborate more your answer adding a little more description about the solution you provide?Uniparous
Run this it gives you all description.Wardrobe
R
0

An option would be waypoints.js, but I think you can do it with some simple jQuery. Waypoints may be for you, may not be... but it could be used in this scenario. So it's an option.

Waypoints.js here: http://imakewebthings.com/waypoints/

Rochellerochemont answered 17/4, 2015 at 21:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.