Manipulating Browser's History With HTML5 History API
Bài đăng này đã không được cập nhật trong 9 năm
Background
Browser provided ways for user to navigate around the web by using something call a URL(Uniform Resource Locator) either by clicking on the link or entering it directly into the browser's location bar. Browser making a request to the server identify by the URL and the server perform some work and send back the webpage to the browser to render resulting in what we call the full page reload. That is how the traditional web works over the year.
Some webpage almost look exactly the same different only in some little change in content, so it seem to be wasteful to request for new page that will cause the full page reload while what we want to do is to only change the part that need to change.
Given this situation that web developer came up with the idea of using others technologies such as AJAX to fetch only the content that need to change combine with some DOM manipulation using jQuery or some other javascript framework to replace new data with old one and display it on the webpage this resulting in faster page load.
But there are limitation with this techique. Suppose the user go to the page and interact with the element on the page that involve using above technique then he/she suddenly want to go back one step by pressing the back button on browser then BOOM! That is where HTML5 History API come in.
What is HTML5 History API?
It is a set of javascript API that provided by the browser in the form of a history
property object that attach to window
object. It lets us manipulate the browser history so that when the user make some change and click the back button but still take back to where he/she was from.
Bellow are the list of public API that history object provide:
The history.pushState()
method
This method take three arguments
- The
state
object is a JavaScript object which is associated with the new history entry created by pushState(). Whenever the user navigates to the new state, a popstate event is fired, and the state property of the event contains a copy of the history entry's state object. title
Firefox currently ignores this parameter, although it may use it in the future. Passing the empty string here should be safe against future changes to the method. Alternatively, you could pass a short title for the state to which you're moving.URL
The new history entry's URL is given by this parameter. Note that the browser won't attempt to load this URL after a call to pushState(), but it might attempt to load the URL later, for instance after the user restarts the browser. The new URL does not need to be absolute; if it's relative, it's resolved relative to the current URL. The new URL must be of the same origin as the current URL; otherwise, pushState() will throw an exception. This parameter is optional; if it isn't specified, it's set to the document's current URL.
The history.replaceState()
method
This method operates exactly like history.pushState()
except that replaceState()
modifies the current history entry instead of creating a new one.
The popstate
event
A popstate event is dispatched to the window every time the active history entry changes. If the history entry being activated was created by a call to pushState
or affected by a call to replaceState
, the popstate
event's state
property contains a copy of the history entry's state object.
Time to practice
The demo can be found here.
First lets setup some basic html structure along with some css. It is pretty basic so no need to explain.
<h1>Navigation Without Refresh <span></span></h1>
<h2>Current Page: <span>/</span></h2>
<ul id="menu-nav">
<li><a href="/page-1/" title="Pagina 1">Page 1</a></li>
<li><a href="/page-2/" title="Pagina 2">Page 2</a></li>
<li><a href="/page-3/" title="Pagina 3">Page 3</a></li>
</ul>
body {margin:10px}
h1 {font-size:20px}
Then next is the actual javascript code that manipulate the browser's history
var setCurrentPage = function(url) {
$('h2 span').html(url || "/");
$("#menu-nav a[href='" + url + "']").fadeTo(500, 0.3);
};
$('#menu-nav a').click(function(e){
e.preventDefault();
var targetUrl = $(this).attr('href'),
targetTitle = $(this).attr('title');
$("#menu-nav a[href='" + window.location.pathname + "']").fadeTo(500, 1.0);
window.history.pushState({url: "" + targetUrl + ""}, targetTitle, targetUrl);
setCurrentPage(targetUrl);
});
window.onpopstate = function(e) {
$("#menu-nav a").fadeTo('fast', 1.0);
setCurrentPage(e.state ? e.state.url : null);
};
OK, now let first take a look at setCurrentPage
function.
All it does is change the content of span
element that is a child of h2
element to whatever the url that passed in and add the fadein animation effect to make it look nice nothing fancy.
Next is we bind the click event handler to all link elements contain within div
. This event handler does three important things first it prevent the default link behaviour to stop it form making the actual request to the server in order to prevent page reload, second is the use of window.history.pushState
to add new entry into browser's history with the state
object that has one property name url
to the current clicked link's href
attribute value, title
to the current clicked link's title
attribute value and the final url
argument to the value of href
to make the browser's location bar change to this url
and the thrid is make the change to the DOM element by using the above setCurrentPage
function that we've just discussed. We also add fadeout animation effect to the link element whose href
value contain the last url in browser' location bar before we change it using history.pushState()
.
Now whenever we click on the link it will show the value of href attribute that we clicked in the span
that is the child of h2
and also change the browser's address bar to make the user think that we actually making the request for new page load.
So to complete the picture of tricking the user into thinking that he/she actually make pull page load we need to deal with the problem when user click on the back button. That is what to final pices of code does.
The final pices of code is the used of window popstate
event. As you can see in the discussion above about when this event is fire so all this code does is to attach event handler to this event and restore to the old state. We can access to the state object that we pass in when we use pushState
as a property of event object and call to the setCurrentPage
function to display it back on page.
Wrap up
Although HTML5 come with a set of powerful History manipulation API it has some limitation. It only support in only the newer version of browser that you can find a list here.
But the good news is that there are some people out there who have write some wrapper library to take advantage of this new functionaly but still make it support older browser, so for further reading about it you can check it out here
All rights reserved