Pushrod

Old dogs, new tricks

Ditch link_to_remote the unobtrusive way

with one comment

So, Rails 2.0 is out, but the somewhat dubious link_to_remote and other javascript helpers are still there, and haven’t been removed into a plugin, as I think was suggested at one point.

I think that’s a bit of a shame, as it encourages some fairly nasty practices, from filling your code with a series of links all with the same javascript code, to linking to a null anchor by default (and so failing to ecourage a non-javascript option), to putting code inline, when it really doesn’t need to be (and you wouldn’t think of doing that with CSS these days, would you?).

Dan Webb has written about this quite a bit and got me turned onto the whole thing at the Euro Railsconf 06 (see his presentation). He is also responsible for the excellent lowpro, a lightweight extension to Prototype that makes unobtrusive javascript a cinch and has just been updated to work with Prototype 1.6.

Time for an example. In Autopendium, the old car community website I run, when you view the page for a particular model, it shows the Youtube videos for that model. Clicking on the description or thumbnail inserts the Youtube embedded viewer above the videos and starts playing it.

Videos on Autopendium

There’s a number of ways to do this — including using pure javascript to write the HTML for the viewer, either from the video’s id or, if you want more details on the video (rating, date uploaded, tags etc) via Youtube’s API (which can return json if requested).

I’ve gone for a server-side solution, which has the added bonus of allowing me to show the video on a separate page, complete with the video’s title in the page’s title, and the video’s tags in the meta tags. Though the googlebot obviously doesn’t play the video, it does see all the other stuff, which all helps a little, and sends a few extra users my way.Functionality on the Rails side is pretty straightforward. There’s a VideosController, and a #show action, which uses the Youtube video id as the :id parameter, and a responds_to block which renders it in its own page if it’s a regular html request, and via an RJS action if it’s a js request.

As far as the links go, I could of course use link_to_remote:
link_to_remote "View video", {:url => {
:controller => "videos", :action => "show", :id => video.id },
:method => :get }

A bit of a mess, and the output is no better:

<a href="/videos/45" onclick="new Ajax.Request('/videos/45',
{ asynchronous:true, evalScripts:true, method:'get'});
return false;">View video</a>

Perhaps we should add a few font tags while we’re at it…

The Unobtrusive alternative is rather nicer (this assumes you’re adding the lowpro library). In the application helper, define a link helper:

def remote_link_to(text, link, options={})
html_class = "remote #{options.delete(:class)}".strip
link_to(text, link, options.merge({:class => html_class}))
end

This is super simple — it simply appends a remote class to your link. So, for the video viewing link we write

remote_link_to "View video", {:controller => "videos", :action => "show", :id => video.id }

which outputs:

<a href="/videos/45" class="remote" >View video</a>

Then in your application.js, add the following:

Event.addBehavior({"a.remote": function(event) {
this.observe('click', function(event) {
new Ajax.Request(this.href, {asynchronous:true, evalScripts:true, method:'get'});
return false;});})

Job done! Now all links with a class of “remote” will make an Ajax request if called by a js-enabled browser, or a regular request if called by a search engine or non-js-enabled browser.The code is neater, shorter, better, and it works so well I’ve extended the pattern for a couple of other helpers

  • external_link_to — which appends a “external” class, which with pretty much a single line of js means that all such links open up in a new window. I’ve also CSS styled this to show an “external link icon”.
  • modal_link_to — open a modal (floating) box with the result of tan Ajax request using the Control.Modal library
Advertisements

Written by ctagg

December 12, 2007 at 5:52 pm

One Response

Subscribe to comments with RSS.

  1. […] Then in the application.js file you need to add the observer to the text input. I’m a great believer in unobtrusive javascript and use Dan Webb’s excellent Lowpro library which I’ve written about before: […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: