<g:plusone count="false" href="http://blog.andrei.rinea.ro/2016/07/18/having-fun-with-html-5-history-api-part-two/" size="small">
As we saw earlier in part 1, hitting the back button into a page with filled in fields will not restore (all) the values, but merely the ones that exist at the time of the page loading. In some cases, for example cascading dropdowns, will not be shown or exist at all at the time of the page load.
What we will accomplish in this part is to push into the query string (without reloading the page!) the selected make and the selected model (where applicable).
We are going to make use of:
history.replaceState(...)
function. There is a W3C standard available for this function (and others). Note that this is supported on all browsers, but only the recent versions.
To do so, we need a few things:
- A function to parse (a) query string; sadly JavaScript does not have anything built in, not even jQuery is of much help
- Change the URL upon changing of selection
- Restore selection from the URL – including cascading
Great! Let’s start.
Upon a bit of searching around which turned into stackoverflowing, I reached the most voted question on this matter called ‘How can I get query string values in JavaScript?‘.
I really looked for a quick-simple-and-standards-compliant-and-so-on solution but the best I could scavenge was this:
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
Ugly, but it works.
We already have a bit of initialization (quick tip/reminder : in jQuery, you can write a function that will execute upon document ‘ready
’ just like so: $(function() { /* … */ }); )
:
$(function () {
$("#SelectedMake").change(makeChanged);
makeChanged();
});
Let’s add an event handler for the change of the selected model in order to change the URL from there, too:
$(function () {
$("#SelectedMake").change(makeChanged);
$("#SelectedModel").change(modelChanged);
});
function modelChanged() {
var makeId = getParameterByName("mkId", document.location.href);
var modelId = $("#SelectedModel").val();
if (!modelId)
history.replaceState(null, null, "/Home/Selector?mkId=" + makeId);
else
history.replaceState(null, null,
"/Home/Selector?mkId=" + makeId + "&mdId=" + modelId);
}
We also need to change the makeChanged
logic in order to simplify the function and allow future reuse of the Ajax populating call (changed lines are highlighted) :
function loadModels(make, pushState, onSuccess) {
$.ajax({
cache: true,
type: 'GET',
url: '@Url.Action("GetModelsByMake")',
data: { 'makeId': make },
success: function (data) {
if (pushState)
history.replaceState(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 makeChanged() {
var selectedMake = $("#SelectedMake").val();
if (selectedMake === "") {
$('#divModel').hide();
} else {
loadModels(selectedMake, true);
}
}
We refactored out the Ajax call into the loadModels
function and added two parameters:
pushState
– This would control whether after successfully loading the models, the URL should be changed or not onSuccess
– An option callback to be executed after successfully loading the models
At this point, if we run the sample, we should have URL changing functionality fully implemented but if we submit into the next page and go back to the selector page only the make dropdown will have its selection restored and that isn’t our work but the browser’s (this worked from the beginning).
Finally, in order to restore selection from the URL, a bit of initialization is necessary:
$(function () {
init();
$("#SelectedMake").change(makeChanged);
$("#SelectedModel").change(modelChanged);
});
function init() {
var makeId = getParameterByName("mkId", document.location.href);
if (!makeId) return;
var modelId = getParameterByName("mdId", document.location.href);
loadModels(makeId, false, function () {
$("#SelectedModel").val(modelId);
});
}
.. and now all should work smooth!
You can check out the code on Github.