AJAX pagination with Javascript
Ryan Bates did an excellent Railscast about pagination with AJAX. I’d like to take it one step further and explain what to do when you want some Javascript functionality in the pagination partial.
Let’s just go with something easy: say you want to highlight clicked products instead of continuing to the show product page. We’ll create a file called highlights.js:
// highlight.js $(function() { function on() { $(this).css('background-color', 'yellow'); return false; } function off() { $(this).css('background-color', 'transparent'); return false; } $('.product h3 a').live('click', function() { $(this).toggle(on, off); return false; }); });
Remember to include the file, we can do this in index.html.erb, right below including pagination.js
<% # index.html.erb %> <% title "Products" %> <% javascript "pagination" %> <% javascript 'highlights' %> <% # more code here... %>
Okay, let’s try it out. First click… Oops, nothing happens. Second click – background turns yellow. Third click – background turns back white. Maybe second page will be better. First click – nothing happens. Second click – there it is, yellow. Third click – white again.
Everything works except for that nasty first click, where did this come from?
Remember when we did this:
$('.product h3 a').live('click', function() { $(this).toggle(on, off); return false; });
What this live thing does is bind a click event to all selected elements. The problem is that toggle does exactly the same thing. So when we click for the first time, the toggle method is run for the first time, and therefore it doesn’t toggle anything, it just binds another click event. From then on everything works like it should. The simplest solution would be to call toggle right after live:
$('.product h3 a').live('click', function() { $(this).toggle(on, off); return false; }); $('.product h3 a').toggle(on, off);
First page – great, works! Second page – oh crap! Apparently everything that’s in the highlight.js file has been done just once on the initial page load, and that includes the toggle line we have just added. It should be done every time you change the page, because there are new links that need to have the click bound. If so, how about adding the toggle line into index.js.erb?
$("#products").html("<%= escape_javascript(render("products")) %>"); $('.product h3 a').toggle(on, off);
Trying the second page – my Firebug says “on” is not defined. That gives me an idea. Let’s change our highlights.js file to this:
function on() { $(this).css('background-color', 'yellow'); return false; } function off() { $(this).css('background-color', 'transparent'); return false; } $(function() { $('.product h3 a').live('click', function() { $(this).toggle(on, off); return false; }); $('.product h3 a').toggle(on, off); });
The on and off functions are now defined regardless of whether the page is ready or not. Check it again – tadaa!
Ok so just to sum up what we need to do from Ryan’s final code:
- create
highlights.jswith this content - link to
highlights.jsinindex.html.erb - call
toggleinindex.js.erb
And actually, while we’re at it, we might remove the live call altogether. We need to bind the click event on our own each time anyway, so this is not needed anymore. So the final highlights.js looks like this:
function on() { $(this).css('background-color', 'yellow'); return false; } function off() { $(this).css('background-color', 'transparent'); return false; } $(function() { $('.product h3 a').toggle(on, off); });
And you know what, I’m just going to say it. AJAX pagination sucks.
