Twitter

Animated scroll to internal links in JavaScript

Posted: July 8th, 2010 | Author: | Filed under: Web Development | Tags: , , | No Comments »

Usually, I’m a big fan of UI libraries like jQuery and YUI. Don’t really like to re-invent the wheel. This is a function that’s quite easy to do in either of those libraries.

Unfortunately, I’m working on a project right now with very strict guidelines on it’s code, so it turns out I am unable to use any JavaScript UI Libraries. So, I set forth on putting this together.

There were a few resources I used that came in handy to create this, and I’ve given credit to those folk at the end of this post.

Part one: setInternalLinks

Run this function on page load. I use Simon Willison’s addLoadEvent to do this. It will search for all internal links on your page, and add a click listener to the animation function scrollToYOffset.

/**
* setInternalLinks
* Searches the page for any internal links and converts them to animated automatic scrolls
*/
function setInternalLinks()
{
	// grab all anchor links on the page
	var anchors = document.getElementsByTagName('a');
 
	for (var i = 0; i < anchors.length; i++) {
		var a = anchors[i];
 
		if (a.href && a.href.indexOf("#") != -1  // href has #
		   && ((a.pathname == location.pathname) || ('/' + a.pathname == location.pathname)) // path name of url is same as current
		   && (a.search == location.search)) { // query string of url is same as current
			var aName = a.href.substr(1, a.href.length - 1);
			a.onclick = function (e) {
				// checks if the caller event e exists and grabs the target (mozilla + webkit). Grabs the IE equivalent if not.
				var target = e ? e.target : event.srcElement;
				// the anchor name
				var anchorName = target.hash;
				// cut out the hash mark
				anchorName = anchorName.substr(1, anchorName.length - 1);
				// grab all the anchors on the page again
				anchors = document.getElementsByTagName('a');
 
				for (var j = 0; j < anchors.length; j++) {
					if (anchors[j].name == anchorName) {
						// found the right anchor. scroll to it!
						scrollToYOffset(anchors[j].offsetTop);
					}
				}
			};
		}
	}
}

Part two: scrollToYOffset

The method below is called by an onclick handler added by the setInternalLinks method. It’s a time-based animation with sinusoidal easing. Smooth and quick.

/**
* scrollToOffset
*
* @param yOffset	The vertical offset to scroll to
*/
function scrollToYOffset(iTargetY)
{
	// if the target is negative set it to 0
	iTargetY = iTargetY < 0 ? 0 : iTargetY;
 
	var frameInterval = 20; // 20 milliseconds per frame
 
	var totalTime = 750;
	// current scroll position: checks if window.pageYOffset exists (webkit + mozilla). If not, set's it to the IE equivalent
	var startY = window.pageYOffset ? window.pageYOffset : window.document.body.scrollTop;
 
	var d = iTargetY - startY; // total distance to scroll
	var freq = Math.PI / (2 * totalTime); // frequency
	var startTime = new Date().getTime();
 
	var tmr = setInterval(
		function () {
			// check the time that has passed from the last frame
			var elapsedTime = new Date().getTime() - startTime;
 
			if (elapsedTime < totalTime) { // are we there yet?
				var f = Math.abs(Math.sin(elapsedTime * freq));
				window.scrollTo(0, Math.round(f*d) + startY);
			} else {
				clearInterval(tmr);
				window.scrollTo(0, iTargetY);
			}
		}
	, frameInterval);
}

Above code has been tested in Firefox 3.6.6, Safari 5 and IE 7.

Credit for some help: Sitepoint: Make Internal Links Scroll Smothly with JavaScript and Cross-Browser.com: Animation Techniques