Quick ScrollTo Code for jQuery

Scrolling to an element in jQuery
Scrolling to an element in jQuery

The Problem

I had been using a jQuery plugin to automatically scroll an element to the top of a container with success for quite a while. Then I changed my markup and the .ScrollTo() function began having strangely; specifically, scrolling my entire page to the top of the window then refusing to respond to further .ScrollTo() calls. After an hour of trying to figure out why and then trying an alternative plugin with the same results, I decided to try and see if I could just quick write what I needed.

It turned out to be surprisingly easy — actually only two lines of code — one if you really want to compact it further.

The Layout

The simple markup we are looking at is a container full of elements that we want to scroll to by clicking various buttons. So let’s say we have a container full of header-paragraph pairs representing short descriptions of planets in the solar system (content taken from wikipedia). We have a toolbar of buttons with the planet labels, and by clicking them, we want to scroll to the appropriate header. The markup would look something like this:


<div id="planets">
  <div id="mercury" class="button">Mercury</div>
  <div id="venus" class="button">Venus</div>
  <div id="earth" class="button">Earth</div>
</div>
<div id="content">
  <div id="planetWrapper"</div>
    <h2 id="content_mercury">Mercury</h2>
    <p>
    Mercury is the smallest and closest to the Sun of the eight planets in the Solar System, with an orbital period of about 88 Earth days.
    </p>
    <h2 id="content_venus">Venus</h2>
    <p>
    Venus is the second planet from the Sun, orbiting it every 224.7 Earth days.
    </p>
    <h2 id="content_earth">Earth</h2>
    <p>
    Earth is the third planet from the Sun, and the densest and fifth-largest of the eight planets in the Solar System.
    </p>
  </div>
</div>

The CSS

There’s only a couple of key elements that need to be in place:

  • The content element, being the “outer container” needs its position set to relative and its overflow-y property set to hidden
  • The planetWrapper element needs its position property set to absolute

The Scroll

I won’t go into all the details here of reacting to the click events and such in jQuery. What I want to show you is the code that scrolls the planetWrapper to the correct element. Let’s call our function scrollToPlanet and it will accept one parameter, which is the planet’s H2 to scroll to. Therefore:


function scrollToPlanet(planetHeader) {
  currentPos = $(planetHeader).position().top;
  $(planetWrapper).animate({
    top: -(currentPos)
  }, 1000);
}

All we are doing here is noting the current position of the planet header we want to scroll to, then animating the entire planetWrapper element such that its top position moves up a negative amount equal to the header’s position. This effectively dispalys the header at the top of the container.

Finding the Bottom of the Page

I ran into a challenge the other day where I had to calculate how much space was left between an HTML element and the bottom of the browser window. We were using Google’s Search-As-You-Type code (http://code.google.com/p/search-as-you-type/) which, according to a fellow developer, “worked like a dream”. He then handed it off to me to implement in another section of the application.

He had been using the search bar at the top of the page with no problems, whereas I needed it further down in a form. I dropped in the code and found that the JavaScript was somehow not calculating a dimension correctly. Depending on where you had scrolled the page to, the search box would either shrink the height of the dropdown results to barely anything, or sometimes nothing at all!

The Google code has a function called updateDimensionsAndShadow(), and that seemed to be the culprit. So after trying to modify what was in there and getting nowhere, I added a small section of code for the script to calculate the dropdown height correctly. Now, the big challenge for me here was that I’ve never had to try and find where the current “bottom of the page” was. I usually worry about the positioning of an element from the top of the page. So here is what I learned, and the code I wrote to fix the height issue.

Finding the Bottom
Finding the Bottom

The first thing we do is find the y-coordinate for the top of our input element of our search box, relative to the top of the document. This is done by first grabbing the offsetTop of the element which is it’s position relative to the container it’s in. We add the offsetHeight of the input because we are going to actually want the position of the bottom of the input box (that’s where the dropdown list will start).

var sf = document.getElementById('searchField');
var searchTop = sf.offsetTop + sf.offsetHeight;

Next we will cycle through all of the element’s ancestor’s (via offsetParent) and continuously add each of their offsetTop coordinates to our caclulation. This will give us the y-coordinate of the bottom of the search box relative to the very top of the document.

var sfParent = sf.offsetParent;
while (sfParent) {
 searchTop += sfParent.offsetTop;
 sfParent = sfParent.offsetParent;
};

Next we will get the position of the bottom of the browser’s toolbar, relative to the top of the document.

var yOffset = (window.pageYOffset) ? window.pageYOffset : document.body.scrollTop;

We can now finally calculate the max height of our search results dropdown list (refer to the diagram).

var maxSearchResultsHeight =
document.documentElement.clientHeight - (searchTop - window.pageYOffset) - searchAsYouTypeConfiguration.bottomPageMargin;

What we are saying here is “The height we have available is [the visible height in the browser] minus [the difference between the top of the element in the document and the bottom of the browser toolbar in the document] minus [the margin we want between the droplist and bottom of the browser]”.

Now the one thing I do need to fix yet is cross-browser compatibility (cough, IE, cough, cough), but the above modifications appear to be working in the real browsers.

Please let me know if you have a better way of finding the bottom of the page.