I'd be curious to hear from anyone who thinks that this sort of parallel hashchange/pushState support is a bad idea. It should prove to be controversial at the least.
People who don't care much about Internet Explorer support often say to just use pushState, and let IE users endure full page refreshes ... but that's not really an acceptable way to build a responsive web app.
Hopefully, having the same structure for pushState and hash-based URLs, with transparent upgrades in both directions, is a sane way to bridge these sort of applications to the future.
> People who don't care much about Internet Explorer support often say to just use pushState, and let IE users endure full page refreshes ... but that's not really an acceptable way to build a responsive web app.
Why not? It's responsive for those with a modern browser (and may get the IE team to integrate the history API in IE10), and is less responsive for those with an older browser. That can be an acceptable tradeoff.
Sure -- it depends where you draw the line for "acceptable", and how much you care about the experience of your IE users. In any single-page web app of significant size, I'd argue that it's never acceptable to do a full page refresh. Would you really want to go back to a GMail that refreshed when you clicked around between labels?
Regarding (2): hash-based URLs also work perfectly well for older browsers.
you're right, "acceptable" is subjective. but it doesn't seem unreasonable that if you are using an outdated version of a program, it will not perform optimally. to that end, managing both pushState and hashchange adds overhead and complexity. personally, i don't think enhancing old browsers is worth said overhead.
that said, if it was part of a framework i was using anyway, overhead and complexity are much less of an issue, so the question becomes "why not".
I'd just like to see a little better documentation on this in the Backbone docs. The documentation you currently have says I'll need to alter the backend, but it's not exactly clear what the changes to the backend would need to be. Do I need to implement /new and /edit on my server to make Backbone's pushSate work, or do I need them implemented to provide for a graceful fallback? It's a little confusing as written so I had to just code something up to see what happened.
I'm suspicious of certain possible scenarios here. Someone without the history state API (e.g., an Internet Explorer 8 user) who wants to send out links winds up publishing fragment-based links (with all the scattered interoperability breakage thereof), but others get and publish full(-flavor) links. Then someone finds that with more restrictive JavaScript settings, some of the links work and some of them don't, or bookmarks two references to the same thing, or…
It seems like it would have potential for confusion, but it strongly depends on the application and its context. It's possible for the author to partially work around this by providing what amounts to an extra URI indicator (similar to “permalink” markers in blogs and other time-dependent views of content stores), but this is awkward. (Is there an add-on library for that, for that matter?)
Those two URLs don't result in the same behaviour. After clicking the first, you have to click back three times to go back to the current page. This would surprise most users.
Right now I'm doing pushState manually and I have a bug that I can't figure out. popstate happens whenever history changes, including when the user types my URL in and hits enter. There doesn't appear to be a way to differentiate between hitting the back button and going to my URL directly. Which is a problem because if they type it in directly I want to render on the server, if they hit the back button I want to do xhr. What am I doing wrong?
As for Backbone, I really need to switch to this rather than manually handling click events and feeding jquery tmpl. Backbone seems to be a much better way to do it but there also appears to be a learning curve that I'm fighting against going through.
> Which is a problem because if they type it in directly I want to render on the server
Why? What difference does the precise way they navigated to the URL make?
> if they hit the back button I want to do xhr.
Most back buttons have a popup menu which let users go to a completely arbitrary url they've already seen, they're not limited to the very last page. They can also hit the button several times to go back to the same page.
> What am I doing wrong?
You're adding arbitrary separations between two instances of the same event: your users going from your site to your site.
They are not arbitrary separations, they are very real. When a user navigates from the URL bar or from a link on another site the browser requests a specific location from my server. Since I'm already serving the browse HTML it makes sense to render my templates at the same time. It doesn't make sense to send only a generic template and then have the popstate trigger an XHR request for some JSON which gets rendered by a javascript template library. That's extra and unnecessary.
What are you suggesting? That I request the entire html doc at every popstate event?
> When a user navigates from the URL bar or from a link on another site the browser requests a specific location from my server. Since I'm already serving the browse HTML it makes sense to render my templates at the same time. It doesn't make sense to send only a generic template and then have the popstate trigger an XHR request for some JSON which gets rendered by a javascript template library. That's extra and unnecessary.
Wait, I might have misunderstood something here: do you mean `popstate` is triggered when users arrive on your site from an other one or from nowhere? On initial loading? As in, it's triggered by navigation between different domains, not just in-domain navigation?
edit: Try checking the `state` attribute of your event object, it should only be set (therefore truthy) for history entries created via pushState, urls being navigated will not have a state.
Yep, I was doing it wrong. You're supposed to pass a state object as the first parameter, I was passing null. So this fixes 2 things, prevents me from having to do XHR on initial page load AND I don't need to do an XHR on back/forward navigation either, I can just use the state object to store my data. Awesome!
Hah, that might be what Spine would like you to think ;)
In all seriousness, Spine is Alex MacCaw's rewrite/re-imagining of Backbone for his O'Reilly book: http://jswebapps.heroku.com/
The broad strokes are roughly the same, but the internals work differently. I'd suggest you look at what both libraries have to offer, and pick whichever suits your fancy. The benefits accrued by your application should be similar in both cases.
Spine doesn't depend on Underscore.js, but also doesn't benefit from Underscore's rich collection functions. If you'd like to each, map, filter, find, reject, every, some, invoke, include, sortBy, without, or pluck over your models, try Backbone.
If Backbone looks too bewildering at first glance, Spine may be easier to start with: the documentation is certainly better geared for beginners.
Or you could use progressive JavaScript, AFAIK. Make your every link a first class citizen (with it's own view) and for supported browsers replace links' default action with content switching (without reloading) using JS requests.
Of course that's a bit harder since your views need to respond in a different manner to casual GETs and AJAX requests.
Which really should be trivial using most modern 3-Tier frameworks. They universally have a concept of "layout" vs "template" so in a pseudocode logic, all you need to do is tap into the render method and swap out an empty layout when responding to an XHR request.
* http://www.documentcloud.org/public/#search/guantanamo
* http://www.documentcloud.org/public/search/guantanamo
People who don't care much about Internet Explorer support often say to just use pushState, and let IE users endure full page refreshes ... but that's not really an acceptable way to build a responsive web app.
Hopefully, having the same structure for pushState and hash-based URLs, with transparent upgrades in both directions, is a sane way to bridge these sort of applications to the future.