167

I'm writing an AJAX app, but as the user moves through the app, I'd like the URL in the address bar to update despite the lack of page reloads. Basically, I'd like for them to be able to bookmark at any point and thereby return to the current state.

How are people handling maintaining RESTfulness in AJAX apps?

Rais Alam
  • 6,970
  • 12
  • 53
  • 84
jasonjwwilliams
  • 2,541
  • 3
  • 19
  • 14

8 Answers8

117

The way to do this is to manipulate location.hash when AJAX updates result in a state change that you'd like to have a discrete URL. For example, if your page's url is:

http://example.com/

If a client side function executed this code:

// AJAX code to display the "foo" state goes here.

location.hash = 'foo';

Then, the URL displayed in the browser would be updated to:

http://example.com/#foo

This allows users to bookmark the "foo" state of the page, and use the browser history to navigate between states.

With this mechanism in place, you'll then need to parse out the hash portion of the URL on the client side using JavaScript to create and display the appropriate initial state, as fragment identifiers (the part after the #) are not sent to the server.

Ben Alman's hashchange plugin makes the latter a breeze if you're using jQuery.

C8H10N4O2
  • 18,312
  • 8
  • 98
  • 134
Dave Ward
  • 59,815
  • 13
  • 117
  • 134
  • @buymeasoda Thanks for the edit; I'm not sure what I was thinking when I wrote that part. – Dave Ward Apr 25 '11 at 02:40
  • 1
    Wow, this answer was very helpful. Although the URL examples are a bit confusing, SO might be automatically replacing the links with titles. How about replacing those with something like example.com and example.com/#foo, so that we can see the entire url in plaintext. – Neil Jun 20 '11 at 17:14
  • @Neil: I did purposely use the /questions/page structure at the time, but I'm not entirely sure why. You're probably right that keeping it simple is more clear. I updated the answer accordingly. – Dave Ward Jun 20 '11 at 17:32
  • @Dave I want the #foo without the hash-char is that possible? – Pascal Apr 19 '12 at 21:35
  • 4
    @Pascal You can, but only in browsers that support HTML5's pushState. Good info on that here: http://diveintohtml5.info/history.html – Dave Ward Apr 20 '12 at 04:46
  • 1
    I ended up being a big fan of history.js to cover the bases for browser compatibility https://github.com/andreasbernhard/history.js/ – jocull Feb 11 '13 at 16:01
  • Looks like you're right. That's the only approach. This seems like a good detailed explanation of your advice: – jasonjwwilliams Aug 04 '08 at 18:04
18

Look at sites like book.cakephp.org. This site changes the URL without using the hash and use AJAX. I'm not sure how it does it exactly but I've been trying to figure it out. If anyone knows, let me know.

Also github.com when looking at a navigating within a certain project.

daniel.wright
  • 277
  • 2
  • 5
  • I noticed that with GitHub a week or so ago. How on earth do they do that? Must go back and check. – Drew Noakes Feb 14 '11 at 01:46
  • 16
    They seem to be using the javascript function pushState(). This adds to your browser's history. Look into it; it's pretty interesting and cool. – daniel.wright Mar 02 '11 at 03:04
  • Thanks for this, it was exactly what I was looking for. I had wondered how github did it. At first I thought it must be reloading but there were only AJAX requests. In Opera, it did reload the page. – kamranicus Apr 29 '11 at 15:12
  • Similar technique is used by fb while navigating through pages of different groups. You have a left nav column in which different groups are mentioned. When you click on a particular group name the url changes and only the content of the main content pane changes along with the url(no hash). So when the user reloads the page they are not redirected to the home page but to the groups' page. – Madeyedexter Jul 19 '13 at 15:12
17

It is unlikely the writer wants to reload or redirect his visitor when using Ajax. But why not use HTML5's pushState/replaceState?

You'll be able to modify the addressbar as much as you like. Get natural looking urls, with AJAX.

Check out the code on my latest project: http://iesus.se/

Laurel
  • 5,965
  • 14
  • 31
  • 57
iesus
  • 171
  • 1
  • 2
11

This is similar to what Kevin said. You can have your client state as some javascript object, and when you want to save the state, you serialize the object (using JSON and base64 encoding). You can then set the fragment of the href to this string.

var encodedState = base64(json(state));
var newLocation = oldLocationWithoutFragment + "#" + encodedState;

document.location = newLocation; // adds new entry in browser history
document.location.replace(newLocation); // replaces current entry in browser history

The first way will treat the new state as a new location (so the back button will take them to the previous location). The latter does not.

  • Is there a way to add an entry to the bookmark history without refreshing the page? Setting the location (as in your first example) will cause a refresh, won't it? – Drew Noakes Sep 22 '10 at 10:42
  • doing document.location.replace will also redirect the browser to that url (just tried it in chrome console) – Omu Jun 16 '12 at 10:06
3

SWFAddress works in Flash & Javascript projects and lets you create bookmarkable URLs (using the hash method mentioned above) as well as giving you back-button support.

http://www.asual.com/swfaddress/

3

If OP or others are still looking for a way to do modify browser history to enable state, using pushState and replaceState, as suggested by IESUS, is the 'right' way to do it now. It's main advantage over location.hash seems to be that it creates actual URLs, not just hashes. If browser history using hashes is saved, and then revisited with JavaScript disabled, the app won't work, since the hashes aren't sent to the server. However, if pushState has been used, the entire route will be sent to the server, which you can then build to respond appropriately to the routes. I saw an example where the same mustache templates were used on both the server and the client side. If the client had JavaScript enabled, he would get snappy responses by avoiding the roundtrip to the server, but the app would work perfectly fine without the JavaScript. Thus, the app can gracefully degrade in the absence of JavaScript.

Also, I believe there is some framework out there, with a name like history.js. For browsers that support HTML5, it uses pushState, but if the browser doesn't support that, it automatically falls back to using hashes.

Rizwan
  • 103
  • 4
  • 24
Neil
  • 3,100
  • 5
  • 29
  • 36
3

The window.location.hash method is the preferred way of doing things. For an explanation of how to do it, Ajax Patterns - Unique URLs.

YUI has an implementation of this pattern as a module, which includes IE specific work arounds for getting the back button working along with re-writing the address using the hash. YUI Browser History Manager.

Other frameworks have similar implementations as well. The important point is if you want the history to work along with the re-writing the address, the different browsers need different ways of handling it. (This is detailed in the first link article.)

IE needs an iframe based hack, where Firefox will produce double history using the same method.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
2

Check if user is 'in' the page, when you click on the URL bar, JavaScript says you are out of page. If you change the URL bar and press 'ENTER' with the symbol '#' within it then you go into the page again, without click on the page manually with mouse cursor, then a keyboard event command (document.onkeypress) from JavaScript will be able to check if it's enter and active the JavaScript for redirection. You can check if user is IN the page with window.onfocus and check if he's out with window.onblur.

Yeah, it's possible.

;)

Rizwan
  • 103
  • 4
  • 24
Marcelo
  • 73
  • 1
  • 6