Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

Having Fun With HTML 5 History API – Part Three

5.00/5 (4 votes)
22 Jul 2016CPOL2 min read 6.4K  
Having fun with HTML 5 history API - Part 3
<g:plusone count="false" href="http://blog.andrei.rinea.ro/2016/07/22/having-fun-with-html-5-history-api-part-three/" size="small">

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:

JavaScript
history.pushState( ... )

and an event called popstate:

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

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

JavaScript
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:

JavaScript
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:

JavaScript
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)