#175 AJAX History and Bookmarks
Browser page history and bookmarks do not usually work with AJAX requests, but in this episode I show you how to remedy this problem using a simple jQuery plugin.
- Download:
- source code
- mp4
- m4v
- webm
- ogv
Looks great, thanks. Do you use jquery to the exclusion of protoype now?
sc
You're my hero :D
great screencast again! very clean solution. briliant!
As always very cool!
One question you or others may know the answer to:
Is it possible to let tools like 'Google analytics' to pick up on the traffic the pages get, when it's all done in JS?
@vesters yes it is
search for "track javascript events google analytics" you can even track flash events
@vesters it's something like: 'pageTracker._trackPageview('/pageNameHere');' pageNameHere can be nested with slashes and can be any identifier/label you want.
Phew! I just finished watching all 175 episodes (took me a couple of weeks) and now I'm up to speed. Ryan, it's fantastic that you make such a terrific resource available to us for free -- it's much appreciated!
The question now is.. Should I migrate from prototype to jquery? jquery shows a lot of progress these days but the ajax helpers in Rails are for prototype:(
Firstly, thanks Ryan for the part-2.
@Fredd, you could use jRails to make the helpers work with jquery seamlessly.
Thanks for a useful series Ryan. One question though: why not rewrite the URL to just include the query string rather than a fragment? It seems that would eliminate the need to have an event listener for refresh/bookmarking.
@Seth, I do primarily use jQuery, but sometimes I will still use Prototype depending on what I'm doing.
@Fredd, I think it's good to learn both libraries and see which you feel most comfortable with. As Millisami mentioned you can use jRails to add helper methods for jQuery, but I prefer to stick with the unobtrusive approach in jQuery.
Also, the javascript helpers in Rails 3 will be unobtrusive and library agnostic (so it will work with jQuery).
@Jeff, IIRC changing the url through javascript triggers a page reload, so that is why you can only change the fragment.
Ryan, thanks for another great screencast. Although not specifically about ruby or rails, javascript is certainly something we all do and could get better at.
Your solution in jQuery is great; really simple and extremely readable.
For the both of us that use Mootools, this looks like a good solution that will follow the same principles described in this screencast.
http://digitarald.de/project/history-manager/
Incredible episode, thanks!
The last episodes were instrumental for me in somewhat abstracting the episode code to put ajax requests on all links between an .ajax_frame tag without explicitly specifying the params to pass:
http://pastie.org/586433
Or are there any problems with it resp. cases where ajax links are not working? :-)
Ryan, here's a scenario that is be a bit ugly.
Say someone opens page 2 in a new window, thus having the url:
/products?page=2
Then if they click on the page 3 link they have the url
/products?page=2#page=3
Ewwww. In this case the likelihood of this happening is low, but in my app it's pretty likely.
Any ideas to avoid this?
The following scenario concerns me.
Person A has JavaScript enabled.
Person B has JavaScript disabled.
A browses the pagination and tells B to enter the same url.
B will end up on the first page with no notice.
The controller must be modified to redirect #page=x to ?page=x before loading the page. Right?
@Murdoch, nice abstract solution. I think the full URL in the hash is a little ugly, but the simplicity is great. Also you should remove the jQuery.get call on line 3.
@Goldy, unfortunately I don't know of a good way to avoid the double page params. You could use javascript to remove the "?page=2" portion but that will cause a page reload.
@Nils, the fragment (#page=x) does not get sent to the server, so there's no way we could parse it in the controller. I don't know of a solution here unfortunately.
Guess I'm an aging Luddite, but what is the advantage of using AJAX here? All the important page content is reloaded by a trip to the server anyway, and you have to manually (well, using the utils plugin) handle all the page history stuff that ordinary page requests do automatically. What does this relatively complicated alternative win you?
You could use a dynamically generated script tag to get the pagination data. This is cleaner in IE where using XmlHTTPRequest triggers an Active-X warning.
Hi Ryan,
Thanks so much for your reply. I must say it's quite an ego boost for me knowing that your thinking matched mine :) Many thanks for your screencasts and your work on rails in general.
@Stan - I think this is a very cool technique - I consider pagination just an example. (Indeed I'm not using it as such.) Just look at what gmail achieves with it.
I love all your episodes, I can't describe the quality of them with words, thank you!
@18: You're right that the example in this episode doesn't make much sense for Ajax. You also lose the benefit of client-side caching since every click of the back button is a new request... but the technique is helpful when there are other things on the page that you can't afford to reload: think of the pagination on YouTube comments for example.
Yeah! Thanks i have been waiting for this screencast since i saw previous screencast about AJAX pagination...
Thank you!
Awesome! You solve my problem. Thank You !!!
Once again very usefull screencast. I have implemented it in my new rails website. Having few problems but I will get in fully working.
You must really checkout Sammy (http://code.quirkey.com/sammy). Sammy gives all of this and much more. It can be used alongside this plugin too.
Thanks for this awesome Railscast Ryan! One small question remains: I'd like to set the fragment to a certain string like '#/action/subaction'. Any ideas how to manage this without having some key-value pair in my url?
Thanks and greetings,
Joe
Hello Ryan (and guys),
instead having
/#page=2
/#page=3
I'm trying to have
/#projects
/#tasks
Basically I'm trying to filter my elements by keyword, but I have hard time to achieve so
Is there a way to create such a URL?
Thanks
Awesome! I almost gave up doing AJAX based pagination then this comes out!
How did your get to know so much?
I turned off Javascript and the site still works without the AJAX pagination. So I guess this is unobtrusive and SEO friendly too.
I'm fairly positive that this comment will get lost amidst all the spam, but I just want to mention that these two new plugins: jQuery BBQ and jQuery urlInternal supersede URL Utils. I've refined and simplified the hash / history API significantly, and separated the functionality into two logical, complimentary plugins:
jQuery BBQ:
http://benalman.com/projects/jquery-bbq-plugin/
jQuery urlInternal:
http://benalman.com/projects/jquery-urlinternal-plugin/
Check them out if you haven't already!
The URL Utils example described in the screencast seems so much simpler than attempting to implement similar features using the jQuery BBQ and jQuery urlInternal plugins - am I missing something or has this project taken a step backwards?
Kevin--
It took me a little bit to wrap my mind around the changes in Ben Alman's JavaScript, but in the end it turns out to be similar in length and complexity.
Here's a clean interpretation of the pagination.js using jQuery-BBQ: http://gist.github.com/358429
Tebrikler arkadaşlar
thanks for the post. I'm using the new BBQ library and that's also very easy to use.
Other fantastic alternatives include jQuery History which provides nice tracking of hash changes for your website. And jQuery Ajaxy which allows you to have a rich ajax website while still supporting the back and forward buttons and no server side changes.
jQuery History: http://www.balupton.com/projects/jquery-history
jQuery Ajaxy: http://www.balupton.com/projects/jquery-ajaxy
Great solution But ....
There is a # in the url which is ok if you are linking to content on the same page but totally naff if you are linking to other pages.
It seems however that there is no way of doing this without using a # in the url which leaves the problem still to be solved IMO.
Yes, great for pagination but when you have pages that you want to link to it's just wrong.
If anyone has any idea on how to do this without the # in the url I'd love to hear it.
Why?
I want to load the specific content for each page when a link is chosen without doing a refresh of the static content in the headers, side bars and footers. Yes caching takes the pain away but I'd rather have the AJAX solution for the effects of a web 2 style site.
I have tried to get your tutorial to work with JQuery BBQ but after 3 hours not getting past the first step I give up...
http://benalman.com/projects/jquery-bbq-plugin/
If you find time, an update for Rails3/Current JQuery+JQuery BBQ would be superb. I love your casts.
It seems that with iceweasel there are problems using history.pushState.
My javascript code is:
$(function () {
$('.pagination a').live('click', function () {
$.getScript(this.href);
history.pushState(null, "", this.href);
return false;
});
$(window).bind("popstate", function() {
$.getScript(location.href);
});
});
with chrome it calls, for example, index.js.erb and it is right, while with iceweasel it calls index.js.erb and then also index.html.erb.
I've found that jquery.ba-bbq.js works well than history.pushState
@ewall - thanks for the code on making this work with BBQ (post 34). It works great.
@Ryan - Big fan of your Railscasts. Thanks a ton!
this episode looks obsolete after #246, but still good exercise, thank you!