Saturday, August 8, 2009

Making a smoothly sliding div that moves on page scrolling

One of the sexier features of mapyeti is the sliding map on the main page. It's actually a really easy effect to implement. For this tutorial we'll be using Prototype and Scriptaculous, although you should be able to adapt this to whichever flavor of javascript you like without too much work.

*note: sorry about the code formatting; I'll figure out how to format this stuff properly later

The Effect
I'm sure you've seen pages that use Javascript to force a part of the page to have a fixed position while scrolling. Horrible Idea. The fixed element is always jittery during the scroll and it's really distracting and just unpleasant in general. A much better way to achieve a similar effect is to wait until the user stops scrolling, then slide the div smoothly to the new position. It's must less distracting, and when you see it happen you get a warm, fuzzy feeling inside :).

Step 1: Building the sliding action
For the sliding action, we want to make our div slide up to the top of the user's browser screen. To Do this, we'll use document.viewport.getScrollOffsets() From Prototype. This function returns how far the user has scrolled in pixels as an array. In our case, we're just interested in the vertical scroll offset, or document.viewport.getScrollOffsets()[1]. Calling this will tell us exactly how far the user has scrolled so we know how far down we have to move our sliding div to maintain it's position on the page.

Next, we'll use the scriptaculous function Effect.move to slide the div down smoothly to it's new position. Let's put it all together:

function slide_that_div(){
var scroll_offset = document.viewport.getScrollOffsets()[1];
new Effect.move('sliding_div', { y: scroll_offset, mode: 'absolute' });
}
The first parameter to Effect.move is the id of the div you want to slide (or the div itself if you prefer), and the second parameter is a hash of options. In our case, we're just moving in the y-direction and using absolute positioning. This means that the position we specify will be relative to the top-left corner of the page, rather than the current position of the div.

Step 2: Handling the Scrolling Event
Now we have a function we can call to make our div slide into the correct position on the page, so our next step is to figure out when the user has finished scrolling. There are several ways you can do this. Unfortunately there is no built-in "scrollend" event, so we have to figure out when the user stops scrolling manually. The easiest method is to just poll every half-second or so and see if the scroll offsets are changing. We'll use the document.viewport.getScrollOffsets()[1] method described above to measure the scroll offsets.

To do an action at set intervals in javascript we use the setInterval() function. This function takes 2 parameters: a function to call repeatedly and a number representing a period in ms. We're going to want to initialize this timer when our page first loads, so we'll put all this stuff into a document.observe("dom:loaded") event handler. The "dom:loaded" event gets called when all the elements in the page are first loaded.

Putting it all Together we have:

var scrolling = false;

var last_scroll;


document.observe("dom:loaded",function(){

setInterval(function(){

var current_scroll = document.viewport.getScrollOffsets()[1];

if (scrolling){

if (current_scroll == last_scroll){

scrolling = false;

slide_that_div();

}

} else if(current_scroll != last_scroll){

scrolling = true;

}

}, 500);
last_scroll = current_scroll;
});


There are 2 variables we need to keep track of. "scrolling" is a boolean that says whether or not the user is in the process of scrolling. "last_scroll" records the last scroll offset we measured. We compare this to the current scroll offset to determine whether or not the user is scrolling.

What this function is saying is if we weren't scrolling and the scroll offsets don't match, record that the user started scrolling. If the user was scrolling and the scroll offsets match, then the user has stopped scrolling so we should fire off our "move_that_div()" function to slide our div into place.

That's all there is to it! Simple, right? If you want to see this in action just take a look at the map on the mapyeti main page. Enjoy!

4 comments:

  1. Do you have a version of this using the jquery library?

    Setting this up in Drupal, and Drupal uses jquery.

    ReplyDelete
  2. Man I copied the code you listed and placed all of the script calls in my head tag and nothing happend. It doens't do anything. Suggestions?

    ReplyDelete
  3. Can you provide any more information? Are there any javascript errors? Do you have a div with the id "sliding_div"? Can you check manually using firebug that the "slide_that_div()" function is working correctly?

    ReplyDelete