Time Traveling & Finding a Use for .pushState( )

When you type “this” into your browser’s javascript console, you’re returned an object:

Window {external: Object, chrome: Object, document: document, GoogleAnalyticsObject: "ga", waffle: Object…}

While debugging your code, seeing this object could cause frustration (and likely has at some point) - you may have forgotten to bind “this” to the function in question or something along those lines. In this context, the appearance of a wild “Window” is nothing more than a sign that you’ve done something wrong.

Up until this point, I’ve not interacted with this “Window” object in any formal capacity - brief investigations reveal that it holds a bunch of information pertaining to the browser, screen size, and a torrent of other unknown attributes. It wasn’t until a googling rabbit hole stemming from Turbolinks lead me to the javascript “pushState” method that I learned how interactions with the “Window” can be incorporated into code to have a web app do cool things. This post will primarily focus on interacting with the “history” attribute, however I’m certain that many of the others are as useful and interesting.

The “history” object (accessible via window.history in your browser console), in short, provides an interface for manipulating the browser’s session history. The most simple examples of this manipulation are via the window.history.forward( ), window.history.back( ) and window.history.go(-3) methods, allowing you to navigate the browser forward and back through the pages in the user’s session history, as well as target a specific location.

Digging further, we find .pushState() and .replaceState(), which allow you to add and modify history entries. From MDN:

Using history.pushState( ) changes the referrer that gets used in the >HTTP header for XMLHttpRequest objects created after you change the >state. The referrer will be the URL of the document whose window is >this at the time of creation of the XMLHttpRequest object."

A simple way to see this in action is to enter the following into your developer console:

1
2
var stateObj = { foo: "bar" };
history.pushState(stateObj, "", "bar.html");

Woah - the url changed but you didn’t go anywhere. Now check your browser history - it added this phantom page url as well. This is cool, but how could this be useful? A use case could involve leveraging jQuery to fetch and load page templates while having .pushState( ) feign page changes, resulting in a much faster user experience than typical full-page reload. This strategy would really only be effective on applications that share a layout (for header/footer/styling) across many pages.

Using a very, very simple multi-page app example, we could use the following code snippet (drawn from a post by Ross Penman) to execute what was outlined above:

1
2
3
4
5
6
7
8
9
10
11
12
$(function( ) {
  var $main = $("main");

  $('a').on("click", function( ) {
    var href = $(this).attr("href");

    history.pushState({ }, '', href);

    $main.load(href + " main");
    return false;
  });
});

As discussed, the result of incorporating this code is the illusion that our page is reloading at blazing speeds, when in reality we’re only pulling and rendering the <main></main> content from each page clicked with the “.load( )” jQuery method.

There’s an issue with this code though - while we get the initial effect we wanted, the user isn’t able to use the back button. In order to do that, we’d need to utilize AJAX and check the “popstate” of the page - a window event that is updated whenever the active history entry changes (in this case, right before .pushState( ) is called). The full code for this functionality can be found on Ross Penman’s illustrative post, with some great explanation of the methodology.

I still have a lot to learn about this topic, as well as a lot of questions - for example, how incorporating the use of .pushState( ) into an app would affect cross-browser performance.

Resources: