The following properties are listed with their associated object as they relate to scroll position…
window.pageXOffsetAlias for the
window.pageYOffsetAlias for the
window.scrollXDistance from outer edge of browser the viewport scrolled horizontally
window.scrollYDistance from top of the viewport to the top of the scrollbar. Value will vary as you scroll downward.
window.scrollY(currently broken in WebKit based browsers)
document.body.scrollTopWide support, but deprecated in strict mode
Everyone loves something that sticks on scroll and in the following example I use
window.pageYOffset detection to “stick” our centered text when the text reaches the top of the viewport.
To start things off I grab some variables and geometry as it’s always good to do the least amount of work inside a scroll handler (performance reasons). We retrieve the header and title selector along with both of their heights.
At this point I determine how far the scrollbar has moved using
window.pageYOffset (you could use
scrollY as well). If the scroll position is greater than
(header_height - title_height ) / 2 then a CSS class is added otherwise we remove the class.
Looking even closer, all the above takes place when the viewport has scrolled enough to have the top of the text touch the top of the viewport. This means when
window.pageYOffset is equal to the distance between the top of the window and the top of the text the text will “stick.”
To get at the math we know the text is in the middle of the viewport, so the vertical distance between the top edge of the text and the middle of the viewport is 1/2 it's height (refer to diagram in figure 4). This means the distance between the top of the window and the top of the text is 1/2 the viewport height (the vertical distance between the top of the viewport and middle middle of the viewport) minus half the text height. This is how we arrive at
(header_height - title_height ) / 2.
In this example
scrollTop is used to detect our trigger point at which we stick the main navigation to the top of the viewport. In this case we look for our
scrollTop distance to report greater than or equal to the hero height minus the banner height. If this value is met then we toggle a class and show the nav strip.
To determine my point of execution I gather what the height of the banner and the hero is in order for me to get at my offset value (distance between my starting and ending point). In this particular instance I'm using the jQuery method
$(window).scrollTop(), but you could also use
document.body.scrollTop if you desire a vanilla approach. You can see all this taking place in Figure 6.
Fast forward to current times and the results are far different now that iOS 8 has been released. As you can see in this screencast the scroll event reacts no matter if my finger is on the screen or not (http://cl.ly/image/3V0Z1N1R290B). The execution now triggers as it should (as the event takes place).
Tom Moitié discovered a strange result with
window.scrollY when using dual monitors and shifting the position of the main monitor (found under system preferences on Mac). The same happens for
window.scrollLeft with two monitors. For example, I have an Apple Studio Display (yup you heard that right) with a Samsung as my secondary monitor. The monitor on the right (Apple Studio Display) reports
window.screenLeft as 0, but the Samsung monitor reports -1920 when I drag my browser over to that monitor (Samsung).
References & Such
Might be good idea to debounce that scroll event you got there 🙂
Hi Marco. In my tests I find that debouncing only delays the execution of scroll events. For example, the Fill Murray nav on scroll appears as it should, but is delayed from it’s initial trigger point. The effect becomes more jarring and the point of execution is missed entirely.
That might be true. You would still get (a lot) less calls on the
navSlidethough, having less overhead and possibly better FPS. Still, you could improve it further to make sure it’s optimized which reminds of
Thanks for sharing this thought out post! I specially like your diagram example, always nice to visualize the problem 🙂
You’re welcome Marco. Thanks for reading and sharing your tips plus starting a great discussion. It seems rAF in this particular case isn’t all that helpful. Chrome now schedules 1 scroll event per frame, just before rAF. You can see that paint times and FPS are far better without rAF.
Is it possible to get an image to get pinned to the middle of the page and then after scrolling down more it gets unpinned?
Absolutely Sergio. I’d suggest taking a look at this demo by ScrollMagic. Essentially you’re setting a duration value in pixels for the distance between the trigger point and the release point.
Hi Its ok with one element but how to do it when we have array of section. means when section reached to viewing area i wish to add effects. how can i do that?
Sorry for the delayed reply as this comment got lost in the sea. Would be helpful to see an example, but here are some CodePen Demos that might help and are also used for the ScrollMagic Wiki Tutorials.
scroll detection is confusing. Thanks a lot