We've been working on a couple of projects lately in which we wanted their statically positioned sidebars to stick to the top of the page when you scroll down. When the first project came along, I said "... I can do this" right before writing some custom javascript with hard-coded values. When the second project came along, my instinctual hatred for writing the same code over and over again kicked in and I wrote StickyScroll—a surprisingly (to me at least) flexible and compact jQuery plugin to take care of the heavy lifting.
The main idea here is that these sticky elements need to be contained in some way. Obviously, when scrolling up a page, the sticky element needs to stop sticking when it returns to its original position. But I would argue that almost equally important is having a bottom boundary on the page where the sticky element will stop sticking so that it doesn't spill into the footer or even outside of the page itself. With no options provided, the plugin will use the <body> element as the bottom boundary. If you want to customize the bottom boundary, there's two approaches you can take:
Say you have the following page structure:
<div id="header">
...
</div>
<div id="container">
<div id="content">
...
</div>
<div class="sticky">
...
</div>
</div>
<div id="footer">
...
</div>
You probably want the div.sticky element to be contained within the div#container.
$('.sticky').stickyScroll({ container: '#container' });
One of the snags I came across was having an element that I wanted to be sticky, but it didn't have any special parent element to be contained within. In this case, there is a "manual" mode where you can specify a pixel offset, relative to the bottom of the document, as the bottom boundary for your sticky elements.
$('.sticky').stickyScroll({ mode: "manual", bottomBoundary: 150 });
In this example, the bottom of the sticky element will never be closer than 150 pixels away from the bottom of the document.
If you have use for it, try it out and let me know what you think.
position: relative will be set on the container. StickyScroll does not alter the styling of anything other than the element it is called on.position: fixed styling on the sticky element, so IE6 is not supportedIf you missed it before, here's the Github page.
Nice plugin, KB, the reason it's not working is because there is a syntax error in the script. Line 82 should be changed to simply "};" without ().
A few kinks for me though:
My div is 1 of 2 columns (each 49.1% wide). Due to the plugin the div's width increases on first scroll.
Secondly, when I scroll back up it goes beyond it's original position. I would want it contained to where it was if possible.
And thirdly, an "easing" animation option instead of this fixed design would be great.
I'm currently using http://www.wduffy.co.uk/blog/keep-element-in-view-while-scrolling-using-... until the kinks are worked out here. That one works well but it's not at the top of the body boundary unlike yours. There seems to be a big margin-top there.
Well, I was able to customize the snippet from the site I linked to do what I wanted, it may interest you:
var $scrollingDiv = $("#preview-div");
var elemTop = $scrollingDiv.offset().top;
$(window).scroll(function(){
$scrollingDiv.stop();
if ($(window).scrollTop()>elemTop) {
$scrollingDiv.animate({"marginTop": ($(window).scrollTop()-elemTop) + "px"}, "slow" );
} else {
$scrollingDiv.animate({"marginTop": "0px"}, "slow");
}
});
@Wesley and @KB, looks like my most recent commit of stickyscroll really messed some things up. I'm working on a fix, and I'll let you know when it's up.
The updated version with demo pages is up at http://github.com/rickharris/stickyscroll.
@Wesley - I like that the solution you found doesn't require fixed positioning, especially since fixed positioning doesn't quite work how you would expect on the iPhone/iPad. I'm not sold on animations, as I imagine there would be a delay between scrolling and the sticky element moving to the position you just scrolled.
Thanks, Rick! Works like a charm.
I was trying to modify another script that used the animation approach for the reason you stated: there had to be a delay between the scrolling and animating as otherwise the div would jerk up and down while scrolling. I'll most likely build a separate mobile/iPhone/iPad version for this project, but the issue with fixed positioning is good to know.
Hey! Thanks for the plugin. But unfortunately, I have uncovered a bug!
It works when the web layout is designed with a fixed grid. On a fluid grid layout, it would work, however, the location where the fixed container will stop upon scrolldown will vary because the content's height will expand/contract depending on the user's window size.
My webdesign is using a fluid grid, if any ideas, please help me!
You're right -- I didn't consider fluid layouts. I'll try to add support for them.
Rick...
First off, thanks for the code!! I was incorporating it today and noticed this. Hopefully you can follow me around this explanation. I have a page that starts short with no scroll bars and then grows longer if a user selects a checkbox that grows the page to a length with scroll bars. Browser is Safari so the scroll bars are hidden until the page needs them. Before the page grows the sticky div works as expected. After the page grows and the scroll bars are added and you scroll down the div does move with the top of the browser but it also jumps to the right approximately the distance of the added scroll bars. I assumed this had to do with the leftOffset. I've moved the leftOffset to inside the onScroll() function so that it is constantly getting calculated. I did this with no knowledge of the consequences but now it works like it should. Here is the code. It'd be great to know if this is a bad thing or a good thing. http://pastie.org/1158890
Thanks Mark!
I can see where the plugin, as it is now, wouldn't react well to dynamically changing layouts, especially horizontal ones. Your code looks logical, I'll test it out and if it still looks good I'll add it in.
In the meantime, if you don't like having that extra calculation every time the scroll event gets called, you can add html { overflow-y: scroll; } to your global CSS file to force a scrollbar, even when you don't need it. IE7 does this by default, and believe it or not, I've come to prefer it. It makes going through pages that may or may not require scrolling more consistent.
Hey Rick, I definitely appreciate the effort help to make this plugin.. but I assure you, once you get it working, it's gonna be the bomb!
Any news on the Fluid layout workaround?
No progress. I have looked at it and tested out some ideas, but I think support for fluid layouts is going to be left out for now.
I have two ideas for how I'm pretty sure I could make it work, but one requires extra markup and one would cause a momentary flash of style, potentially causing a flickering/disappearing sticky element whenever the window is resized, and I'm not satisfied enough with either of those compromises to add support in.
You are, though, more than welcome to fork it on Github and add what you need to it :)
Good news!
The most recent version of StickyScroll has support for fluid layouts! I just pushed it to Github. Let me know how it works out for you
Is there a way to use this so, for example, a div is placed on the bottom-right of a screen and fixed, and when you reach the footer of the page, the div then scrolls up and stays atop the footer? Also, if the footer is high on the page (as if the content weren't enough to take up the full page and make it scrollable), could that div still sit atop of the footer?
I apologize, I'm a total newbie when it comes to java.
:-/
No need to apologize! We all were beginners at some point.
First things first: we're working with Javascript here, not Java. Similar names, but very different languages.
Secondly, no, StickyScroll doesn't do what you're describing. This plugin really only applies to elements that start out statically positioned on the page (that is to say, not fixed at all), and then become fixed (or "sticky") to the window as you scroll past them). The plugin doesn't really help if the element in question starts out fixed.
What you want to do and what StickyScroll does for you are pretty similar, though. It would be possible to adapt the plugin or extract some code to do what you need. Lines 69-120 in jquery.stickyscroll.js are the main chunk of it. It's pretty poorly commented, so I apologize in advance for that. I'll try to add some helpful comments soon.
Hmmmm, maybe I didn't mean fixed, per se.
To give you a visual, it's kinda like a puzzle. This element (approx. 150px x 150px), when you load the page, looks fixed to the content wrapper, like 50px from the bottom and 50px from the left of the wrapper's border. As you scroll it stays put, until you reach the footer (approx. 300px). Once it hits the footer, it moves with the rest of the document to the bottom. When you scroll back up, it returns to it's set position on the screen.
I hope that gives you a better idea of what I am thinking.
I've been searching for upwards of 15 total hours and I can't find anything that's just right. All tutorials I've seen are basically written to keep things toward the top of a page, I just don't know how to convert them to work on the bottom.
Yeah, that's pretty much what I was imagining. The problem is that StickyScroll is meant to do a pretty specific thing well, and not try to solve every single problem. So, it's really good at making a specific kind of sticky sidebar, which doesn't include one that sticks to the bottom of the page, like what you're describing.
Like I said, the logic from StickyScroll could be extracted and edited to do what you need, but I can't promise that functionality will be incorporated into the plugin. Sorry to disappoint!
Its a great plugin to have however, there doesn't seem to be a way of positioning the element you want to be sticky. For example, i'm using this sticky plugin to allow the nav bar (that sits at the top of my page), to scroll with the user until a certain point on my page, which I have working fine. When the page is initially loaded the positioning of my nav is how I want it to look (40px from the top) but as soon as I start to scroll down the page it jumps to the very top of the browser window & remains there. How do I ensure my nav stays 40px from the top always? I can't seem to style this sticky element when it is active?
Any help appreciated!
How do you position the element initially? Since StickyScroll takes care of any relative/absolute/fixed positioning for you, be sure that your element starts out statically positioned and use margin or padding (margin is probably the more correct answer, but padding is probably safer with StickyScroll) to position it, and everything should be great.
Still no luck i'm afraid. Its position is fixed but even after changing it to static, it still reproduces the same problem. It also then interferes with the rest of my style sheet & the way i've coded up the page as the nav then disappears behind other elements. Here is what I have for the nav element (i.e. the element I would like to be sticky):
/* primary */
nav { position: static;
background: url(../images/get-to-know-me.png) top left no-repeat,
url(../images/side-projects.png) top right no-repeat;
padding-top: 40px;
height: auto; }
span.nav-title { background:url(../images/side-projects.png) top center no-repeat; }
nav ul { width: 960px; margin-left:0; }
nav ul, nav li { display:inline; float:left; }
nav ul li { width:120px; }
nav ul li#right-inner,
nav ul li#right-outer { float:right; }
nav ul li a,
nav ul li a:visited,
nav ul li a:active {
padding: 1.2em 0 1.4em 0;
color: #464239;
letter-spacing: .17em;
font-size: 1em;
text-transform:uppercase;
text-decoration:none;
text-align:center;
display:block;
line-height: 1em;}
nav ul li a span,
h1 span.subtitle {
text-transform:none;
letter-spacing:normal;
color: #a23b12;
display:block;
font-style:italic;
font-size:72%; }
/* /primary */
Does anything seem out of place to you?
Hmm. A few things:
Hope this helps!
Thanks for your help on this, i'm not that great at scripting & this probably really basic but how do I implement the following code (taken from the usage instructions on GitHub):
jquery_collection.stickyScroll({ topBoundary: '100px', bottomBoundary: '200px' })
I used the above & added it to $('#prim') so it looked like:
$('#prim').jquery_collection.stickyScroll({ topBoundary: '100px', bottomBoundary: '200px' })
but it didn't work at all.
the jquery_collection just represents what is returned by $('#prim'), so you can drop it. I've updated the README on Github to be more clear.
Hola. Ive been using this for a few weeks now, but it seems the latest version of chrome has a problem with it now?
Yes, indeed. The issue is already on github. I'm looking into possible solutions.
Rick,
Hola. yeah I also posted the issue on github. I really like stickyscroll and if you need any help testing solutions let me know!
Great script but it doesn't seem to be working in IE, maybe I missed it in the comments but is there a solution?
hi,
does stickyscroll support only top positioned divs or also a left navigation for example!?
does the script effects only when the user is on an ipad/iphone/ipod?
best
derby42
I tried this one but its not working properly with my Mozilla latest version as well.Suggest me the solution please.
I really need this to work with jquery 1.3.2 but cant get it done, isWindow is not a function jquery.contains has the same error...
Well, there's two problems here, really. The first is that it's not meant to be used with anything less than jQuery 1.4. The second being the StickyScroll isn't really meant for use anymore. It's broken in the latest versions of Safari, Chrome, and Firefox, and for now I'm taking it as a sign that man was not supposed to imitate fixed positioning with Javascript. Both of those warnings are up on the Github page. Sorry to disappoint!
It all works you just need tobtweak everything ill send everything later.
Hello Rick,
I have just started to use StickyScroll (initially overlooking the warning on GitHub). Anyway, I had problems in Internet Explorer. After a bit of debugging, I realized that my problem was the call to Date.now, which is not supported by IE until version 9. I couldn't really figure out why the time was needed, so I just tried to remove the call to Date.now(). So I use:
id = index,
instead of
id = Date.now() + index,
According to my tests, this works fine in IE8, Firefox 8.01, Chrome 15.0.874.121 and Safari 5.1.2.
If you can give me a hint about why the date/time is necessary, it would be much appreciated :-)
Best regards,
Lotte Mygind
Great script!
is there any chance to apply some easing, when the position changes?
As far I experienced it' quite jumpy / flickering while scrolling the page.
Sorry, this script is outdated and isn't seeing updates anymore. The flickering started happening some time ago and I never came up with a solution to fix it. There's more info on the github page about it.
For reservations, call
office(800) 890-1702.
Send fan mail to our work location at
300 East Stone Avenue,
Greenville,
SC
29609-5626
USA
34.85984128288701
-82.38952159881592
Lovingly crafted by orangecoat with some rights reserved, and a promise not to spam you.
I would love to see a working demo of this. I'm so far unable to get the script to work within an existing page, so I'm not sure if I'm not declaring the element that should be stuck correctly or am missing CSS (there's no info on the "sticky-process" selector called in the script?). I've been trying to modify existing scripts to do just what this one does, so I would be thrilled to get this working.