pushstate

  • property

{Object}

 

Changes can.route to use pushstate to change the window's pathname instead of the hash.

Object

The pushstate object comprises several properties that configure the behavior of can.route to work with history.pushstate.

Use

The pushstate plugin uses the same API as can.route with only one additional property - [can.route.bindings.pushstate.root]. can.route.bindings.pushstate.root specifies the part of that pathname that should not change. For example, if we only want to have pathnames within app.com/contacts/, we can specify a root like:

can.route.bindings.pushstate.root = "/contacts/"
can.route(":page\\.html");
can.route.url({page: "list"}) //-> "/contacts/list.html"

Now, all routes will start with "/contacts/". The default [can.root.route] is "/".

Using can.route.pushstate.js with the can.Control.route plugin

The can.Control.route plugin is a great way to simplify your code. Not only will it bind the event listeners for routes, but it also automagically prevents clicks on matching links from reloading the entire page.

Here are some examples of binding to pushstate-style routes in a can.Control using the can.Control.route plugin. The first comment in each example is an href for a link that would trigger the route. The second is the contents of can.route.attr(). In each example, the contents of the data variable in the function will be the same as can.route.attr(), but without the route attribute. All of these examples assume that pushstate.root is set to the default of '/':

// Listen to the root route.  See above section on pushstate.root
"route": function(data) {
        // <a href="/">Go Home!</a>
        // {route:""}
},

// A route with a trailing slash
"/files/ route": function(data) {
        // <a href="/files/">Pretend this is a folder full of files.</a>
        // {route:"files/"}
},

// A route with a file extension
"/files/batman.pdf route": function(data) {
        // <a href="/files/batman.pdf">I'm Batman.</a>
        // {route:"files/batman.pdf"}
},

// A route without a trailing slash
"/files route": function(data) {
        // <a href="/files">The files are in the computer?</a>
        // {route:"files"}
},

// A route with a wildcard parameter
"/files/:file_name route": function(data) {
        // <a href="/files/robin.pdf">Holy roasted metal!</a>
        // {route:"files/robin.pdf", file_name:"robin.pdf"}

        // Notice that the :file_name parameter in the route becomes a separate
        // attribute of can.route.  This "wildcard" enables listening for
        // any two-parameter route that begins with 'files'.  /files/test.html
        // and /files/moose would also route here.
},

    // A useless route handler that will never be called.
"/files/joker.pdf route": function(data){
        // <a href="/files/joker.pdf">Smile!</a>
        // {route:"files/joker.pdf"}

        // Why is it useless? Because, while the above href could potentially
        // trigger this route event, the directly preceding route with the
        // :file_name parameter will respond because it comes first in the code.
},

// Three parameters, one a wildcard.
"/admin/contestants/:id route": function(data){
        // <a href="/admin/contestants/1">The price is wrong, Bob.</a>
        // {route:"admin/contestants/:id", id:1}
},

    // Three parameters, two wildcards.
"/admin/:page/:anchor route": function(data){
        // <a href="/admin/testimonials/steve">Steve's Testimonial</a>
        // {route:"admin/:page/:anchor", page:"testimonials", anchor:"steve"}

        // Technically, if the route just before this one was taken out of the code,
        // this route would respond to links like /admin/users/1.  In that case the
        // route attributes would look like this:

        // <a href="/admin/contestants/1">The price is wrong, Bob.</a>
        // {route:"admin/:page/:anchor", page:"contestants", anchor:"1"}

        // But, because the preceding route exists, this route will respond to all
        // three-parameter routes that have the first parameter of admin except for ones
        // where the second parameter is users (ones that start with '/admin/users/'),
        // which are picked up by the preceding route.
},

Watch for errors inside route handlers.

Any error inside of a route handler will cause a page refresh before the error is shown. For example:

    "/admin/users route": function(data){

        // This will cause problems.
        console.log(nonExistentVariable);

        // This will cause problems
        var users = someFunctionWithAnErrorInside();

        // This one won't cause problems.
        console.log(data);
}

In an error-free route handler, all links matching the route would not result in a page refresh, but instead would result in a pushstate change. The errors in the example will cause the page to refresh. The code will run after the refresh and show the error message.

Planning route structure

Complications can arise if your route structure mimics the folder structure inside your app's public directory. For example, if you have a folder structure like the one in this url for your admin app...

/admin/users/list.js

... using a route of /admin/users on the same page that uses the list.js file will require the use of a trailing slash on all routes and links. The browser already learned that '/admin/users' is folder. Because folders were originally denoted by a trailing slash in a url, the browser will correct the url to be '/admin/users/'. While it is possible to add the trailing slash in routes and listen for them, any link to the page that omits the trailing slash will not trigger the route handler.