History and Hash Changes (BYOSP Part 6)

Wednesday, February 22, 2012

It's happened to everyone. You thought you typed "Use Ruby To Capture Screen Shot" into a slide, but somehow you hit the i key instead of the o key and didn't notice. Now the slide is presenting itself proudly in front of an  ultra-conservative Ruby programming crowd who is still amped up from drinking green tea all night.

What to do?

You can open a text editor, fix the HTML, and refresh the browser. The slide is fixed, but the slide show always starts from the first slide so you've lost your position in the deck. The crowd grows even more restless.

Let's see if we can prevent this sort of ugliness.

The first step is to assign every slide an ID when the slide show starts. We'll put this logic in a method named assignSlideIdentifiers and call it from the start method.

var assignSlideIdentifiers = function () {
    $("section").each(function (index) {
        $(this).data("id", index);
    });
};

The method numbers every slide from 0 to N and stores the associated number in each section element using jQuery.data.

Now that we have an ID for each slide, we can update the browser history whenever the presenter changes the current slide.

var updateHistory = function () {
    var id = $("section.current").data("id");
    window.history.pushState(
        { "id": id }, id, "#" + id
    );
};

var onKeyDown = function (event) {

    var handler = keys[event.keyCode];
    if (handler) {
        event.preventDefault();
        handler.action();
        updateHistory();
    }
};    

The pushState method is part of the session history navigation API. The API has spotty support, but you can use Modernizr to test for the presence of the feature and there are shims to provide the API if the browser is not up to snuff.

The end result is the slide ID will appear in the URL fragment (a.k.a the hash) of the browser's address bar. For example, the address of the first slide will look like:

   http://localhost/slides/http.htm#0

When you move to the second slide, the address will change to:

   http://localhost/slides/http.htm#1

When you hit the browser refresh button, it is a simple matter for the script to look at the URL fragment and jump to the proper slide.

var setFirstVisibleSlide = function () {
    if (window.location.hash) {
        moveTo(window.location.hash.slice(1));
    }
    else {
        $("section").first().addClass("current");
        updateHistory();
    }
};

var moveTo = function (id) {
    $("section.current").removeClass("current");
    $("section").filter(function () {
        return $(this).data("id") == id;
    }).addClass("current");
};

With the slide ID in the URL,  we can also use the moveTo method to support another new feature – the ability to jump to a new slide by modifying the URL.

var start = function () {
    assignSlideIdentifiers();
    setFirstVisibleSlide();

    $(window).bind("keydown", onKeyDown)
             .bind("hashchange", hashChange);

};

var hashChange = function () {
    moveTo(window.location.hash.slice(1));
};

The hashchange event has been around for a bit, and allows us to detect if the user types a new slide number into the URL, or follows a link to a different slide. We can also now edit slides on the fly, refresh the browser, and not lose position.

At this point we have a fairly useful slide presentation tool. Should we add animation features next? Or should we refactor some problems in the scripts?

Tune in next week to find out.

For all the code, see github.


Comments
gravatar Doeke Wednesday, February 22, 2012
Come on! The slide should be called http://localhost/slides/http.html
There is something called symmetry ;-)

OK, a bit more serious now: the hash/javascript thing is new for me. I'm actually amazed so much javascript is needed. In html4 one can use a hash with a tag.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!