Rails & Prototype Ajax Back Button Support in IE and FF


#1

So I adapted some of the other methods of using the anchor href hash.
Much of this concept I got from
http://www.mikage.to/jquery/jquery_history.html and adapted to Prototype
javascript.

This method works as follows:
You use a custom helper method, called history_remote, instead of
link_to_remote whenever you want a new ‘page’ that can be navigated to
using the back/forward buttons in the browser, or bookmarked. This adds
another parameter to the Ajax.Request in the onclick of the ahref,
history:true, which tells Ajax.Request to do the back button handling if
it is IE or FF. Other browsers should be unaffected, but without
support for back button.

Add this to your application_helper.rb:
module ActionView
module Helpers
module PrototypeHelper

def history_remote(name, options = {}, html_options = {})
options[:history] = true
function = remote_function(options);
function.gsub!(/asynchronous:true/, ‘asynchronous:true,
history:true’)
link_to_function(name, function, html_options)
end
end
end
end

Add this to your application layout.rhtml:

.... ....

Add the following function to the prototype.js:
/////////////////////////////////////////////////////////////
// this needs to be here even if history is disabled
var browser_id = 0;
ie_history_click = function(hash) {
var newhash = ‘#’ + hash;
location.hash = newhash;
var history = $(“ie_hash_history”);
var iframe = history.contentWindow.document;
iframe.open();
iframe.close();
iframe.location.hash = newhash;
}
////////////////////////////////////////////////////////////

And modify the Ajax.Request request function to this:
request: function(url) {

if (browser_id == 1 && this.options.history == true) {
  ie_history_click(url);
} else if (browser_id == 2 && this.options.history == true) {
  parent.location = '#' + url;
} else {
  // we want to actually call for all other browsers and for

non-history
// links in IE and FF
var parameters = this.options.parameters || ‘’;
if (parameters.length > 0) parameters += ‘&_=’;

  try {
    this.url = url;
    if (this.options.method == 'get' && parameters.length > 0)
      this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

    Ajax.Responders.dispatch('onCreate', this, this.transport);

    this.transport.open(this.options.method, this.url,
      this.options.asynchronous);

    if (this.options.asynchronous) {
      this.transport.onreadystatechange =

this.onStateChange.bind(this);
setTimeout((function()
{this.respondToReadyState(1)}).bind(this), 10);
}

    this.setRequestHeaders();

    var body = this.options.postBody ? this.options.postBody :

parameters;
this.transport.send(this.options.method == ‘post’ ? body :
null);

  } catch (e) {
    this.dispatchException(e);
  }
}

},


#2

Some more info:

Basically this sets up a periodical updater, that checks the location
‘hash’, the stuff after the # sign in a URL. To support the back
button, I convert the :url to a hash instead.

So if your action is at http://localhost/my_controller/my_action/my_id,
you will see http://localhost/#/my_controller/my_action/my_id in your
location bar. When you click a link in IE or FF, the only thing that
really happens is that the location bar url changes, then the periodical
update notices a split second later and makes the actual Ajax.Request to
do the change. (In IE it actually does some more magic with an IFRAME.)
It also works with Ajax.Updater since it uses the Ajax.Request’s request
method.


#3

Small fix: I let ‘debug’ for IE in there. Change the ‘5.2’ in the
init_back_button function, to ‘0.2’.


#4

Stephan W. wrote:

Slain W. wrote:

Small fix: I let ‘debug’ for IE in there. Change the ‘5.2’ in the
init_back_button function, to ‘0.2’.

Your work sounds very useful. I remember when I was looking at the
backbutton a year ago, there were several javascript solutions
available; I think I liked this one
http://www.contentwithstyle.co.uk/Articles/38/ which I found later than

http://www.unfocus.com/Projects/HistoryKeeper/

which I am actually using.

I also remember one approach needed different treatments for FF, IE, and
Safari.

How does the solution you chose compare?

Stephan

They all use pretty much the same technique that this does. This just
makes it really easy to use in Rails/Prototype without having to add
JQuery, or Dojo, or some other framework. Using ‘history_remote’ instead
of ‘link_to_remote’ is dead simple as far as what it takes to convert
your site. It took me all of 5 minutes.

One of the key things about this solution, with my redefinition of
Ajax.Request#request, is that it doesn’t affect page or fragment caching
at all, since the links look identical across all browsers.

Many of the other techniques require you to add a class=“remote” or
similar to all of the links, requiring you to run thru all of the links
on the page and changing the onclick behavior. Because of this, some of
them don’t support ‘backable’ links inside the content updated in an
Ajax.Request (at least without adding some additional javascript either
at the end of your update which can get tricky since the request
completes before the links are rendered).

Also, using the actual url_for stuff as the location bar hash definition
seems to be novel in the ones I’ve reviewed. It makes it easy to know
where you are when debugging, and allows external bookmarks to work
without lots of hoops.

Safari support I will look into. It (and Opera) will work fine on the
same site withOUT back button support, but a different technique is
required for Safari that for my clients isn’t a huge issue since it
still works.

The only mild annoyance right now, is if you bookmark one of these hash
links, when you use that bookmark, it will load the / root page then
load the page you wanted. Depending on how you design your site, you
can tweak the init_back_button function to handle it. If anyone needs
help with that, post here.


#5

Slain W. wrote:

Small fix: I let ‘debug’ for IE in there. Change the ‘5.2’ in the
init_back_button function, to ‘0.2’.

Your work sounds very useful. I remember when I was looking at the
backbutton a year ago, there were several javascript solutions
available; I think I liked this one
http://www.contentwithstyle.co.uk/Articles/38/ which I found later than

http://www.unfocus.com/Projects/HistoryKeeper/

which I am actually using.

I also remember one approach needed different treatments for FF, IE, and
Safari.

How does the solution you chose compare?

Stephan