The maze book for programmers!
mazesforprogrammers.com

Algorithms, circle mazes, hex grids, masking, weaving, braiding, 3D and 4D grids, spheres, and more!

DRM-Free Ebook

The Buckblog

assorted ramblings by Jamis Buck

More inline RJS

12 January 2007 — 1-minute read

In Inline RJS I talked about invoking simple RJS actions right there in your controller. Did you know you can do the same in your views, with (among other helpers) link_to_function?

1
2
3
4
5
6
7
8
# before
<%= link_to_function "Close", "$('some_div').down('form').reset(); $('some_div').hide()" %>

# after
<%= link_to_function "Close" do |page|
      page[:some_div].down('form').reset
      page[:some_div].hide
    end %>

Furthermore, Sam Stephenson showed me a trick where you can use RJS in arbitrary helper methods to generate Javascript code. Don’t like inlining RJS as above? Then refactor it like this:

1
2
3
4
5
6
7
8
module SomeHelper
  def hide_and_reset(div)
    update_page do |page|
      page[div].down('form').reset
      page[div].hide
    end
  end
end

Then, in your view:

1
<%= link_to_function "Close", hide_and_reset(:some_div) %>

That update_page method is defined in PrototypeHelper, and will return the generated Javascript as a string. Beware, though: whether you use update_page or a block passed to link_to_function, making use of inline RJS has a price. It’s fine if you only need to do it a few times on a page, but stick to explicit Javascript if you’re emitting lots of bits like that.

Reader Comments

Very cool. Thanks for these updates—I’ve enjoyed them quite a bit.

I can see that loads of great posts are coming from you Jamis!

link_to_function supports inline rjs?!? Jamis, you just blew my mind.

Jamis, Another gem!

The information, quality and clarity of your posts are fantastic. The best rails related blog by a mile in my opinion.

Thanks, John

Awesome.

I don’t really understand why excessive use of inline RJS is bad, does the generated Javascript slow down the page rendering?

Thanks, John!

Brian, the JavascriptGenerator that RJS uses internally isn’t very lightweight right now, so if you use it alot on a page it will significantly slow down the rendering of your page. We had a page that was doing about 120 (very simple) calls to update_page, and by removing those and replacing them with explicit javascript, we increased the rendering speed by 10x (no exaggeration!). However, a handful of calls to update_page will not significantly impact your render time.

Cool! Shouldn’t we always use helpers in such case, though? I thought the Gods of Rails instruct us to stay away from such code in views?

This is a great post, but I’m confused by the line: page[:some_div].down(‘form’).reset

Some other people have used the “down” and “up” functions on DOM elements, but I can’t find a definition of those functions anywhere. They don’t seem to be part of standard javascript, nor of scriptaculous nor prototype. Does anybody know where I can find their definitions?

-gcn

gcnovus, they are in prototype.js. The “down” method, if given with no parameters, returns the first child element. Likewise, “up” without parameters returns the parent element. If a parameter is given, it is a CSS selector that describes the ancestor (for up) or descendant (for down) node to select. In the example, it will return the first “form” tag that is a descendant of the element with id “some_div”.

It’s really one of the more amazingly useful functions in prototype!

Thanks! I love it when I find things I’ve already written 4 times in open source code. :)

A more on-topic question: is there an elegant way to make this degrade for non-javascript browsers?

Autofocus the first form input on every page of your site:

  • Give the body tag in your application.rhtml an id of ‘application’
  • Call the following code using an onload, DOMLoaded (lowpro), or place it just before the closing body tag (if you like to ignore best practices :):

$(‘application’).down(‘input’).focus();

down() is just the thing I’ve been looking for to use on a small intranet app. Sweetness!

Would there be a way to autofocus the first input that’s wrapped in a div with a class name of fieldWithErrors?

meekish, you might try:

1
2

$$('div.fieldWithErrors input').first().focus();

Note that the above does no error checking (for instance, to see if there ARE any matching inputs), but it would be trivial to add such checking if needed.