Having fun with HTML 5 History API – part three

Part 1 – Description of the problem
Part 2 – simple history replacement
Part 3 – additional history items – this article

As we’ve seen in the previous parts, we can alter the history of the current page navigation by replacing the current URL with a new one (this freedom has limits, of course, and we can’t alter the protocol or the host).

What we’ll explore in this part is creating new history items while not leaving the page. In order to achieve this, we’ll make use of

history.pushState( ... )

and an event called popstate :

window.addEventListener('popstate', function () { /* ... */ });

First we’ll need to replace all replaceState calls to pushState calls. The parameters stay the same:

function loadModels(make, pushState, onSuccess) {
    $.ajax({
        cache: true,
        type: 'GET',
        url: '@Url.Action("GetModelsByMake")',
        data: { 'makeId': make },
        success: function (data) {
            if (pushState)
                history.pushState(null, null, "/Home/Selector?mkId=" + make);
            var models = $("#SelectedModel");
            models.empty();
            models.append($("<option></option>").attr("value", "").text(" -- please select a model -- "));
            $.each(data, function (key, val) {
                models.append($("<option></option>").attr("value", val.Id).text(val.Text));
            });
            $('#divModel').show();
            if (onSuccess)
                onSuccess();
        },
        error: function (xhr, ajaxOptions, error) {
            alert(error);
            $('#divModel').hide();
        }
    });
}

// ...

function modelChanged() {
    var makeId = getParameterByName("mkId", document.location.href);
    var modelId = $("#SelectedModel").val();
    if (!modelId)
        history.pushState(null, null, "/Home/Selector?mkId=" + makeId);
    else
        history.pushState(null, null, "/Home/Selector?mkId=" + makeId + "&mdId=" + modelId);
}

At this point we can run the app and see how changing the make and / or the model alters the URL but also the back button can take us to the previous URL on the page. However, restoration of the selection doesn’t happen upon going back. This is because we didn’t add the event listener yet.

So let’s do that. Add the following piece of code:

window.addEventListener('popstate', function () {
    $("#SelectedMake").off("change");
    $("#SelectedModel").off("change");
    init(function () {
        $("#SelectedMake").change(makeChanged);
        $("#SelectedModel").change(modelChanged);
    });
});

In this event handler we’ll capture the situation when the user hit the back button and the URL is on the same page, practically a synthetical location has been navigated to.

Because the URL changed we need to re-init the page, hence the call to the init function. However, the init function will tend to indirectly alter the URL again because it would change the make or model. This would happen because the change event handlers of the drop downs would fire. To prevent this we’re unsubscribing them first. This is done using the .off(“change”) jquery shortcuts.

We, then, need to call the init function which has an asynchronous part, the AJAX call, and only after the call has successfully ended we need to re-subscribe the event handlers. This is why we call init with a new parameter, a function containing the re-subscription to the change events. Therefore we’ll need to change the init function too:

function init(afterInit) {
    var makeId = getParameterByName("mkId", document.location.href);
    if (!makeId) {
        $("#SelectedMake").val("");
        $('#divModel').hide();
        if (afterInit)
            afterInit();
        return;
    }
    $("#SelectedMake").val(makeId);
    var modelId = getParameterByName("mdId", document.location.href);
    loadModels(makeId, false, function () {
        $("#SelectedModel").val(modelId);
        if (afterInit)
            afterInit();
    });

the changes in the function add some extra functionality:

- clears the selection in the make dropdown if the navigated state does not contain one (line 4)
- hides the model selector if no make is selected (line 5)
- selects a make if the navigated state contains one (line 10)

The navigation works forward too, the popState event will fire in this case also.
At this point the app should function well, handling all navigation states.

This concludes this short series on HTML 5 History API.

You can check out the final stage of the HTML code here.

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

WP Like Button Plugin by Free WordPress Templates