Thursday, September 20, 2007

Setting focus to a form field

Rails doesn't set focus to the first form field automatically so you have to do it yourself with a little unobtrusive javascript. Here's a helper that does it:


def set_form_focus
javascript_tag("Event.observe(window, 'load', function() {
var firstForm = $A(document.getElementsByTagName('form'))[0];
Form.focusFirstElement(firstForm);
});")
end


Uses the Prototype library, so the page must have a javascript_include_tag("prototype") or javascript_include_tag("defaults") or equivalent. The Prototype Form.focusFirstElement requires a form id but my forms often don't have a name or id. So we look for the first form on the page using the DOM function getElementsByTagName. This function returns a node list and it's easier to treat this as an array by passing it to the $A prototype function so that [0] gives the first form. Finally, attach this function to the window load event instead of firing before that. Most browsers would show the focus fine if it fires earlier, but IE ends the whole page to be loaded first.

Place the helper after the element on the page, preferably after the <% end %> tag for the form. One tricky thing: if you use tables to build formatted forms (as I do) make sure the <table> tag is after the <% form_for... or <% form_tag... or <% form_remote_for... tag.

Sometimes I use a formless input field with an associated observer to return results via ajax. To set focus to this field I use this quick helper:


def set_focus(id)
javascript_tag("Event.observe(window, 'load', $('#{id}').focus());")
end


Again, it must be after the specified field on the page and this field must have an id.

8 comments:

Jamie said...

Nice one. Thanks:-)

Chris M. said...

I can't get this to work. When I add the code to the application helper it's rendered properly in the page but Firefox indicates an error on the page.

Using Firebug, here's what it says:

form.findFirstElement() has no properties

focusFirstElement(form login)prototype.js (line 1955)
(no name)()login (line 79)
form.findFirstElement().activate();

I tried tracing through the javascript using Firebug but I'm no JS guru and couldn't figure out what the error was. Any ideas?

Chris

p.latyp.us said...

Hi Chris,
Firebug is telling you have no form on your page prior to the helper or there is no select, input, or textarea field within the form that isn't disabled. Verify by looking in the source (be sure to use right-click "View Page Source") that you indeed have a form and that the helper method is after the first form field.
Let me know! Jim.

Chris M. said...

Jim,

There's definitely a form on the page. I can't post the page source here. Is there some other way I can get it to you?

Chris

p.latyp.us said...

Email me! jim at p.latyp.us
Send the page (not the rails source)(yet!). Jim.

GlennFord said...

I just found this and it was really helpful. Your solution worked beautifully for me. Thanks a lot!

Mark said...

Just found this. Exactly what I needed-works great. Thanks!

tamm said...

Hi,
I like to pass a param to set_form_focus method and have getElementsByTagName looks for the form # i pass to it instead of [0] or the default is 0.
I try to make the following changes, and cannot get it working. I don't know much javascript. Can someone tell what is wrong with my changes? Thanks

def set_form_focus(nthform=0)
javascript_tag("Event.observe(window, 'load', function() {
var firstForm = $A(document.getElementsByTagName('form'))
[nthform];
Form.focusFirstElement(firstForm);
});")
end