More inline RJS

Posted by Jamis on January 12, 2007 @ 01:02 PM

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.

Posted in Tips & Tricks

Comments

Have something to add? Click here to leave a comment.

12 Jan 2007

1. Josh said...

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

2. Piotr Usewicz said...

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

3. Chris said...

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

13 Jan 2007

4. John Ward said...

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

5. Brian said...

Awesome.

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

6. Jamis said...

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.

7. zerohalo said...

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?

8. gcnovus said...

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

9. Jamis said...

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!

14 Jan 2007

10. gcnovus said...

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

11. gcnovus said...

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

16 Jan 2007

12. meekish said...

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!

13. meekish said...

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

14. Jamis said...

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.