The fundamental problem with attempting to transparently support querystrings on the client-side -- touched on a bit in this article, but not explained completely, is this:
Querystrings are a server-side convention for URLs containing parameters -- the browser doesn't traditionally parse them. If you ask for document.location.search, you'll get back an opaque string containing the query part that you'll have to parse yourself.
The point of Backbone's router is to be able to transparently support both pushState-based routing, with real URLs, as well as onhashchange-based routing, for older browsers that can't do real URLs via JavaScript. So, if you add querystring generation and parsing support to Backbone's router (as some plugins do), and use pushState, everything starts off looking peachy. But as soon as you run into an older browser, and try to fall back to the hash-based URL equivalent, you run into trouble:
/app?query=string#home?query=other
... now you have the possibility to have to merge two different sources of query string, and still keep transparent redirects working back and forth between the two schemes.
I'd be more than happy to merge an implementation that supports them -- but that implementation needs to solve this particular problem, and have a relatively bulletproof way of supporting the querystring logic parsed out of real URLs and transferred to the fragment, and vice-versa ... and also has to have a strategy for dealing with URLs of the above breed. The devil, as always, is in the details.
> now you have the possibility to have to merge two different sources of query string
Not really:
if hashMode and queryStringInHash
parseQueryStringFromHash
else
parseRealQueryString
You don't have to merge. Just if you modify the query string and are in "hashMode" then just push the "real querystring" elements you parsed first into the "hashquery string".
The thing is that it has to work cross-browser. If an IE user copies an URL with both a fragment query string and a real query string and then shares it with a Firefox user, the URL should resolve to the same thing (and the same thing the other way around).
Not impossible by any means, but, as Jeremy points out, it involves subtle details :)
> some folks currently like to mount their Backbone.js applications at a root URL that contains a query string
That seems like the root of the problem. It seems like querystrings could otherwise be solved, but this case would certainly introduce a problem for pushState-incompatible browsers.
Is there a reason this kind of app root URL isn't considered bad practice that doesn't need to be supported? Everything about it seems wrong - now you're trying to put part of the path after the querystring.
I love backbone, and it works well for us. We hacked in support for full paths and query parameters and will likely end doing full page reloads for IE8/IE9 (IE10 will support push state anyway)
It seems mighty unfair that we can't have nice things because Microsoft took so long to roll out push state.
Well it's les simple that I though but it's still seems doable. I'll probably try to submit a PR about that. Is there any previous PR or issue that I should rea first ?
Not really a problem... does "app" conflict with "home" in your case? If so, then there's also an issue here... if not, then why not? Also, if not, then why not take the same approach to query strings.
var querystring = {};
//add actual querystring (prior to #) parameters
//add hash's querystring, overwriting any already defined keys
return querystring;
This article about "Javascript web applications" quickly turns into an article about how Backbone's URL handling is bad. The content is really interesting but it feels kind of passive-aggressive.
A problem I have with JS web apps, is that "cool URIs don't change" [1]. While querystrings are a server-side convention, the ability to convert a server-side site into JS without having to rewrite all the old URLs is much more important to me than inventing new URL schemes just for JS apps...
I'm getting rank sick of the growing number of websites killing the functionality of the back button. That, combined with Chromes senseless refusal to implement opening clicked urls that are on another domain in a new tab, means I'm often forced to click-and-hold the back button to get somewhere. Incredibly annoying.
I don't understand at all what you mean. To open pages in a new tab you middle click the link. Pages that automatically open in new tab are annoying. If Chrome has something that stops this, I'm extremely happy. The user should control whether a page opens in a new tab or not, and indeed the user can do that -- left click or middle click.
> On a Macbook trackpad or magic mouse you can also set three-finger tap to middle click (with third-party tweaks like BetterTouchTool).
Ick. I hate doing two-finger taps as it is. I don't believe one should need anything other than a pointing device with one button to browse the web effectively. Needing to hack in ways to simulate extra buttons is bad design. Why not use a keyboard then?
It's not Chrome's fault Apple doesn't put a middle button on their mice like everyone else. Though personally I prefer 3-finger tap to using buttons at all on a PC laptop trackpad.
Even if Chrome always opened external links in a new tab with a left click, you'd still want middleclick functionality to browse the web effectively; I constantly open multiple tabs from the same domain- for instance, to open a HN comments section without navigating away from the front page.
> It's not Chrome's fault people can't code external links right.
People shouldn't have to specify which links are external and which aren't, it should be inferred from the URL being accessed. In the rarer cases where the server is saying to open an external link in the current tab or an internal link in a new tab, that should be specified in the HTML. For the people that want specific global behavior. i.e. all new tabs to be opened in current tab, that should be a configuration option in the browser.
Many times when I'm surfing the web, I'm in my recliner under a blanket, with only one hand peeking out to control the trackpad. I don't want or need to use the keyboard. It works perfectly fine until some stupid website stops the back button from functioning. Login is accomplished using LastPass, hell I can even sign up for new services 80% of the time w/o the keyboard using the form fill function.
That's a terrible use case for a plugin. UI behavior should be well-defined and declaratively specified. Installing a plugin just to tell the browser what to do with a mouse click is total overkill.
Web UI is well-specified. Click is follow hyperlink. Right click is browser context menu. Middle click is open in new tab, also available from the context menu.
Whether a link opens in a new tab without user intervention is up to the link, and is way overused IMO.
I like some of Jeremy Ashkenas work (in fact I use some of it daily), but he has jumped the shark with this one.
Another bad sign is that he also removed lo-dash (a better underscore.js clone, that fixes several issues, is faster, passes all the same tests AND also strives to support Backbone.js) from the backbone docs.
To try and nip this particular thread in the bud -- I think that forks in general, and Lodash in particular, are great. As CoffeeScript has Coco, Redux, Kaffeine, and LiveScript; as Backbone has Spine; Underscore has Lodash. They're fertile ground for exploring different approaches.
My problem isn't with the Lodash project, just with the unfortunate fact that the maintainer has had some incredibly toxic behavior with respect to cooperating around open-source. Without getting into details, as soon as he's ready to collaborate in a pleasant and productive fashion, he's welcome to have his commit access back to Underscore, and merge in whatever he likes.
Hi, creator of Lo-Dash here. Jeremy's comments are off base. I've gladly contributed to Underscore and am responsible for many of the fixes/features in the last few releases. I voluntarily revoked my commit rights to Underscore, without prompting, several months ago because it was awkward trying to move Underscore in a better direction while trying to work with Jeremy, who seemed distracted, lacking the patience to grok issues/comments, often losing his temper in issue/commit comments or trashing me on Twitter/HN. I'd still be contributing via bug reports and feedback if he hadn't recently blocked me.
It's unfortunate he's chosen to spread FUD instead of going head to head on features, compat, perf, dev needs/concerns, or any other relevant area of project comparison :/
Thanks. Didn't knew the backstory -- just read the related issues/commit messages were there's no mention of any of this.
Had I known, I wouldn't say that. Perhaps an open statement from you regarding the issue would help (this HN thread will be lost to the depths of the intertubes after a few days, and it doesn't give more details of what transpired).
This is the third or fourth post I've seen lately on "use the architeture of the web, don't fight it with client-side JS apps." I consider it a welcome swing of the pendulum.
Having bashed my head against Backbone's router over the past few months, I've come to the conclusion that pattern based routing is just the wrong approach for picking a view to render the page. Instead, choose a view based on some property of the JSON returned by that URL (I'm using '@type' as our API will expose linked data through JSON-LD.) Now route information only needs to be specified once, server side.
Querystrings are a server-side convention for URLs containing parameters -- the browser doesn't traditionally parse them. If you ask for document.location.search, you'll get back an opaque string containing the query part that you'll have to parse yourself.
The point of Backbone's router is to be able to transparently support both pushState-based routing, with real URLs, as well as onhashchange-based routing, for older browsers that can't do real URLs via JavaScript. So, if you add querystring generation and parsing support to Backbone's router (as some plugins do), and use pushState, everything starts off looking peachy. But as soon as you run into an older browser, and try to fall back to the hash-based URL equivalent, you run into trouble:
... now you have the possibility to have to merge two different sources of query string, and still keep transparent redirects working back and forth between the two schemes.I'd be more than happy to merge an implementation that supports them -- but that implementation needs to solve this particular problem, and have a relatively bulletproof way of supporting the querystring logic parsed out of real URLs and transferred to the fragment, and vice-versa ... and also has to have a strategy for dealing with URLs of the above breed. The devil, as always, is in the details.