
Why JavaScript web applications should embrace traditional URLs - molily
http://9elements.com/io/index.php/hybrid-javascript-apps/
======
jashkenas
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.

~~~
byroot
> 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".

~~~
epidemian
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 :)

~~~
jashkenas
Right-o. PushState-supporting users copying URLs and sending them to IE8 and
IE9 folks is one thing, as is the opposite direction.

In addition, some folks currently like to mount their Backbone.js applications
at a root URL that contains a query string, for example:

    
    
        todolist.com/app?org=nsa#location

~~~
thedufer
> 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.

~~~
smacktoward
"Doctor, it hurts when I do this."

"Then _don 't do that._"

------
VeejayRampay
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.

------
CornishPasty
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...

[1]
[http://www.w3.org/Provider/Style/URI.html](http://www.w3.org/Provider/Style/URI.html)

------
vinceguidry
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.

~~~
4ad
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.

~~~
vinceguidry
I'm on a Mac, I don't have a middle mouse button.

~~~
dysfunction
Cmd-click. On a Macbook trackpad or magic mouse you can also set three-finger
tap to middle click (with third-party tweaks like BetterTouchTool).

~~~
vinceguidry
> 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?

~~~
dubcanada
No, people not using target="_blank" is bad design. It's not Chrome's fault
people can't code external links right.

~~~
vinceguidry
> 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.

~~~
StuffMaster
> all new tabs to be opened in current tab

I think you're confusing links and tabs. I think you're trying to throw a lot
of extra logic into the browser that doesn't belong there.

------
grimtrigger
I wrote a library to help you get there:

[http://epona.karmanetics.com/](http://epona.karmanetics.com/)

------
coldtea
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.

~~~
jashkenas
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.

~~~
jdd
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 :/

------
cdr
Bravo. jashkenas's reticence to fully support pushState is really frustrating.

------
jrochkind1
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.

------
laurencerowe
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.

------
welder
For an example web app that works just fine with hashtags, see
[https://WakaTi.me](https://WakaTi.me)

Built using patterns from this Backbone.js example:

[http://ahamlett.com/Backbone.localStorage/](http://ahamlett.com/Backbone.localStorage/)

------
VMG
I think this a good time to mention URI.js:
[http://medialize.github.io/URI.js/](http://medialize.github.io/URI.js/)

