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