In an app I’m currently working on, I make pretty heavy use of HTML anchors to not only move to target content on the page, but also to load content via AJAX. With normal HTML anchors, clicking on a “source” anchor (e.g., <a href=”#top”>) will jump to the destination anchor (< a name=”top”>). While this is definitely beneficial, the one downside is that this action additionally adds the anchor (“#top”) to the url string (plus, the “jump” in kind of harsh–not very sexy at all).

Since my app is completely AJAX-ified (meaning that there is no navigation between “pages”), I wanted to find a way to preserve the “jumping” behavior of anchors, but not have my url cluttered with anchors.

Turns out that with ExtJS, this is pretty darn easy.

Getting the Anchors

Now of course, you could always modify all of the anchor links in your files to replace all the “href=#…” with onclick JavaScript events. However, what if you have existing anchors that you don’t want to change? With ExtJS, you can easily find all of them and override the default behavior.  In my app, all of my anchors have the following form:

<a href="#WSXXXXXXXXXXXX">...<a>

Since I know that they all begin with a hashmark and AT LEAST the letters “WS”, I can use ExtJS’s powerful query method to grab all matching anchors in the DOM.  Here’s how I do that:

var anchors = Ext.get("body").query("*[href^=#WS]");
for(var i=0;i<anchors.length;i++) {
   var anc = Ext.get(anchors[i]);

Pretty easy to follow. First, I execute the query method on my target element “body”. This limits the anchors returned to those which are children of this element. Next, in the query method, I use the ^= operator, which specifies that I want to return all elements in the DOM that have an “href” attribute which begin with the characters “#WS”.

Once the query has completed its search, it returns an array of DOM nodes that match the selector.  With this array in hand, I simply loop over the contents of the array, get an Ext.Element for each node (using Ext.get()), and apply a new onclick event method: scrollPage. By default, the scrollPage method will be passed 3 arguments: the Ext EventObject, the DOM element which is target of the event (e.g., the “<a>” tag), and any options specified in the onclick event.

Now that I have new onclick events applied to the desired HTML anchors, I can move to overriding the default behavior of the anchors.

Scrolling the Anchors

The scrollPage() method defined in the new onclick events for the anchors is the core method for handling the scrolling of the page and the overriding of the default anchor behavior.

Here’s the method, in its entirety–I’ll walk through it step by step

function scrollPage(e,el,o) {
   // get the element we're basing the scroll on (the target)
   var el = Ext.get(this.dom.hash.replace(/#/,''));
   // get the container
   var ct = Ext.query("#bodypanel .x-panel-body")[0];
   ct = Ext.get(ct);
   // calculate y position of target element; this is how far we need to scroll
   var yoffset = el.getOffsetsTo(ct)[1];
   // do the scroll	
   // do the scroll

The first three lines are pretty straightforward–just getting the Ext Elements of the target item, as well as the container in which it lives.  Now for the good stuff:

// calculate y position of target element; this is how far we need to scroll
var yoffset = el.getOffsetsTo(ct)[1];

Here, I use the method getOffsetsTo() which returns the x,y offsets between the target element (el) and the passed element (ct, in this example). In other words, this method tells me how many pixels my target element is from the left and top bounds of the parent element.  This is important to retrieve because in the next method, I use the vertical offset to tell Ext how far to scroll the element:

// do the scroll	

Simple. scrollTo() tells Ext to scroll the target element to the top, to go X number of pixels before stopping, and animate the movement.  This all results in a really nice, fluid scroll to my anchored content, unlike the abrupt “jump” of normal anchor links.

Stop the Jump

If I stopped here, the default behavior of the link anchor would still fire, and the smooth scrolling of Ext would be drowned out in the hard “jump” of the anchor–plus, the “hash” value would be appended to the url…precisely what I was trying to avoid. So, the final step is to stop the default behavior of the anchor, and this is done simply as follows:

// call preventDefault on the original click event to prevent anchor jump

Because of the event handler I added to the link, the scrollPage() method is passed a reference to the event object. To stop the normal behavior of the anchor, I simply invoke preventDefault() on the event object, which cancels out the default “jump” and URL manipulation. With the default behavior cancelled, the smooth scrolling of the Ext approach can run unimpeded, and the final result is a nice, animated scroll…as well as a pristine url 🙂