JQuery - adding a class to / after a certain point in a scroll with variable length contents above

Pretty unexpected for jQuery and JavaScript in general. I made fun of an example of my problem at http://jsbin.com/alibi3/2/ - with an explanation below.

I have a div that, after scrolling the user past a certain point on the page, is assigned the "fixed" class, so it follows the user. This works fine on its own.

The problem is that the contents above this div can be switched to show / hide - and when it is shown, the fixed class is still applied at the point at which it would be if it were hidden, so it seems to be "jumping "

How to specify the function of adding a fixed class, which was shown / hidden in the div above, and, thus, configure the point at which the "fixed" class is added?

Thanks.

HTML:

<div id="drawer"> <a href="#">Click here to toggle drawer</a> <p id="drawercontents">Here is the stuff in the drawer; hidden by default.</p> </div> <div id="article"> blah, blah... </div> <div id="nav"> This should follow down the page once we scroll past the start of the article, and 'stick' back in place when we are back at the top. </div> 

CSS

  #article { width: 400px; float: left; margin-right: 20px; padding: 20px; background: #eee; } #nav { width: 200px; float: left; background: #ff0; padding: 20px; } #drawer { width: 660px; padding: 20px; color:#fff; background: #000; margin-bottom: 10px; } .fixed { position: fixed; left: 460px; top: 0px; } 

JavaScript:

 $(document).ready(function() { $('#drawercontents').hide(); $('#drawer a').click(function(e){ e.preventDefault(); $('#drawercontents').toggle('fast'); }); var top =$('#nav').offset().top - parseFloat($('#nav').css('marginTop').replace(/auto/, 0)); $(window).scroll(function () { // what is the y position of the scroll? var y = $(window).scrollTop(); // whether that below the start of article? if (y >= top) { // if so, add the fixed class $('#nav').addClass('fixed'); } else { // otherwise, remove it $('#nav').removeClass('fixed'); } }); }); 
+4
source share
2 answers

Whenever you do something that modifies the base position of this “fixed” div, you need to re-capture its base position.

For example, in your demo code, redefine the top of the toggle() call.
See the modified code below or see it in action at http://jsbin.com/alibi3/8 .

 var GblTop; function GetVertOffset (srchStr) { GblTop = $(srchStr).offset().top - parseFloat($(srchStr).css('marginTop').replace(/auto/, 0)); } $(document).ready(function () { $('#drawercontents').hide(); $('#drawer a').click (function (e) { e.preventDefault(); $('#drawercontents').toggle('fast', function() {GetVertOffset ('#nav'); } ); }); GetVertOffset ('#nav'); //-- Sets GblTop $(window).scroll (function () { // what is the y position of the scroll? var y = $(window).scrollTop(); // whether that below the start of article? if (y >= GblTop) { // if so, add the fixed class $('#nav').addClass('fixed'); } else { // otherwise, remove it $('#nav').removeClass('fixed'); } }); }); 
+2
source

I thought for myself! In case anyone else has the same problem and comes across this, here is my revised code and the best (albeit amateurish) explanation of what I did:

JS:

 $(document).ready(function() { $('#drawercontents').hide(); var top =$('#nav').offset().top - parseFloat($('#nav').css('marginTop').replace(/auto/, 0)); $('#drawer a').click(function(e){ e.preventDefault(); $('#drawercontents').toggle('fast', function() { top =$('#nav').offset().top - parseFloat($('#nav').css('marginTop').replace(/auto/, 0)); }); }); $(window).scroll(function () { // what is the y position of the scroll? var y = $(window).scrollTop(); // whether that below the start of article? if (y >= top) { // if so, add the fixed class $('#nav').addClass('fixed'); } else { // otherwise, remove it $('#nav').removeClass('fixed'); } }); }); 

HTML:

 <!DOCTYPE html> <html> <head> <script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <meta charset=utf-8 /> <title>JS Bin</title> <!--[if IE]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <style> article, aside, figure, footer, header, hgroup, menu, nav, section { display: block; } body { margin: 0; padding: 0; } #article { width: 400px; height: 1000px; float: left; margin-right: 20px; padding: 20px; background: #eee; } #nav { width: 200px; float: left; background: #ff0; padding: 20px; } #drawer { width: 660px; padding: 20px; color:#fff; background: #000; margin-bottom: 10px; } .fixed { position: fixed; left: 460px; top: 0px; } </style> </head> <body> <div id="drawer"> <a href="#">Click here to toggle drawer</a> <p id="drawercontents">Here is the stuff in the drawer. It is hidden by default. When it is shown there is a problem with the nav jumping to the top of the page when we scroll.</p> </div> <div id="article"> <h2>This is a long article!</h2> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit pellentesque sed egestas id, iaculis ut erat. Praesent ut neque vel dolor lacinia eleifend at sit amet sem, etc etc</p> <p>Div height has been set to 1000px to force scrolling without adding too much filler text</p> </div> <!-- end article --> <div id="nav"> This should follow down the page once we scroll past the start of the article, and 'stick' back in place when we are back at the top. </div> </body> </html>​​ 

Working example on JS Bin - http://jsbin.com/alibi3/9

top set as a global variable, so it can be used between functions. First, it is set to load the document, then it is redefined whenever the cursor switch function $('#drawer a').click(function(e) launched.

So, whenever $(window).scroll , it has the correct top value to work and behaves the way I want.

0
source

All Articles