Social Media
More About This Website

My name is Wayne Robinson and I'm a web applications developer from Queensland, Australia. In August 2005 I discovered Ruby on Rails and instantly fell in love. From that point forward, Ruby on Rails has been my language of choice for new projects however, I still use PHP to maintain some legacy applications.

Categories
Login
« TIBCO General Interface | Main | Javascript Graphing »
Saturday
Nov112006

Multiple Values from Scriptaculous' Autocomplete

Ever wanted to extract multiple values from a Script.aculo.us Google Suggest-like autocomplete text field? I recently did and here's how.

If you aren't aware of how to use autocomplete text fields, please read over the simple and customised demos available at Script.aculo.us. Also, a warning, this demo utilises Ruby on Rails however, with a little bit of modification, this should work using the Script.aculo.us library without Ruby on Rails.

This example will auto-populate a state and postcode based on the user's selected suburb (yes, I'm Australian).

The first step is to create a view for the page containing the autocomplete field and for the autocomplete field itself.

Controller:

def new
# This is the main controller that will
# contain the autocomplete field
@contact = Contact.new
end

def auto_complete_for_suburb
suburb = params[:suburb]
@surburbs = Suburb.find_by_name(suburb,
:order => "name", :limit => 20)
render :partial => "auto_complete_suburb"
end

View for the new action (assume an application.rhtml template has been created, this template must include the Prototype and Script.aculo.us javascript libraries):

<%= form_tag({:action => :create}, {:method => :post}) %>
<table>
<tr>
<th>Name:</th>
<td><%= text_field(:contact, :name) %></td>
</tr>
<tr>
<th>Suburb:</th>
<td><%= text_field_with_autcomplete(:contact, :suburb,
:select => "value",
:after_update_element =>
"function (ele, value) {
$("contact_state").value =
Ajax.Autocompleter.extract_value(value,
'STATE');
$("contact_postcode").value =
Ajax.Autocompleter.extract_value(value,
'POSTCODE'); }
") %>
</td>
</tr>
<tr>
<th>State:</th>
<td><%= text_field(:contact, :state, :size => 10) %></td>
</tr>
<tr>
<th>Postcode:</th>
<td><%= text_field(:contact, :postcode,
:size => 10) %></td>
</tr>
</table>
<%= end_form_tag %>

Before we continue any further, it is worth-while defining the _auto_complete_suburb.rhtml partial.

<ul class="suburbs">
<% unless @suburbs.nil? -%>
<% @suburbs.each do | suburb | -%>
<li>
<%= h("#{suburb[:name]}, #{suburb[:state]}" +
"#{suburb[:postcode]}") %>
<div class="value" style="display: none;">
<%= h(suburb[:name]) %>
</div>
<div class="STATE" style="display: none;">
<%= h(suburb[:state]) %>
</div>
<div class="POSTCODE" style="display: none;">
<%= h(suburb[:postcode]) %>
</div>
</li>
<% end -%>
<% end -%>
</ul>

The autocompleter view has three (3) hidden <div> tags which contain the extra data used by the base Autocompleter Javascript methods as well as the new one (extract_value) that will be defined below. 

You may also want to cast your eye over the suburb field definition in the new contact view as this is where most of the action is. There are two options to note:

  • the :value option which specifies the class name of the element which contains the value to place in attribute (the default would be whatever is within the rendered <li> field which, as we will find out below, will contain more than just the selected suburb)
  • the :after_update_element option which specifies a piece of Javascript to execute when the item is selected. You will see that this Javascript executes the Ajax.Autocompleter.extract_value function twice. This function does not exist in Script.aculo.us but is provides an easy way to extract extra values from an autocomplete list.

The Script.aculo.us Autocompleter methods conviently pass the complete contents of the <li> field to the method specified in the :after_update_element option. This allows us to extract any additional values from this data. I have created a simple addition to the Ajax.Autocompleter class below that speeds this extraction:

Additional method for Ajax.Autocompleter class. This can be declared anywhere after the inital Script.aculo.us script inclusion. For my purposes I put this at the top of my application.js file. 

Ajax.Autocompleter.extract_value = 
function (value, className) {
var result;

var elements =
document.getElementsByClassName(className, value);
if (elements && elements.length == 1) {
result = elements[0].innerHTML.unescapeHTML();
}

return result;
};

 So that's all there is to it. If anyone would like me to create a demo of the above code, ask me and, if I get enough requests, I'll put something together.

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (11)

Very helpful. I reckon this'll get a flogging from me. Thanks from a fellow Aussie.
November 28, 2006 | Unregistered CommenterJohnny
Hi Wayne

Please provide me the live demo of the example will auto-populate a state and postcode based on the user's selected suburb (yes, I'm Australian). If you will also provide the same script in php & ajax, it will be very helpfull for me. I have urgent need of this so please do the needfull asap.

Regards
Rajeev
January 3, 2007 | Unregistered Commenterrajeev
Thanks for this article (from another Aussie in Darwin!).

A couple of things I had to modify in order to get things working as normal:-

1. I had to change the view script to:-

<%= text_field_with_autcomplete(:contact, :suburb,
:select => "value", {}, { :after_update_element => "function (ele, value) ...etc.

Note the extra curly braces straight after the model and column name.

2. I had to ensure that the Javascript only used single quotes, as using double quotes caused the Ruby interpreter to throw a wobbly.

3. I had to trim down the _auto_complete_suburb.rhtml partial file to remove all spaces and line breaks, otherwise I would get unwanted spaces in the returned string from the autocompleter.

Other than that, thanks for a fantastic resource which certainly saved me hours of research.

Cheers,
Devan
April 23, 2007 | Unregistered CommenterDevan
This was really helpful. I'll probably be adding a new form options helper method using this. If you want the code, just send me an email.
August 3, 2007 | Unregistered CommenterJon
Have you tested it in Firefox 3? I trying but is not working. The textbox shows all the data inside the hidden divs.
June 28, 2008 | Unregistered CommenterMiguel XT
Thank you for this. This saved me a lot of time and digging in the prototype code.
July 2, 2008 | Unregistered CommenterS2
Yes, I use this on Firefox 3. I'm not sure how compatible this is with the latest version of Prototype though and I've made some other improvements over the months. This is a little old now. I will probably release and update over the coming weeks.
July 18, 2008 | Registered CommenterWayne Robinson
I'm migrating some stuff over from an old project where i was using your code. Seems like the extract method no longer assigns the selected value. Not sure what the issue is.
September 2, 2008 | Unregistered CommenterSandy
This javascript actually does not work unless you filter until you have only one <li> left.
If you have like 3-4 and choose one of them the if (elements && elements.length == 1) will not be true, since the getElementsByClassName seems to return all divs from all li`s

/MartOn
September 8, 2009 | Unregistered CommenterMartOn
No, because the value of value (now that's confusing!) contains only the contents of the <li> that the auto-completer selects and, whilst the method is called document.getElementsByClassName, if you pass in a HTML node as the second parameter, it uses this as the parent.
September 8, 2009 | Registered CommenterWayne Robinson
this function works. Change the document.get... with value.get...

Ajax.Autocompleter.extract_value =
function (value, className) {
var result;
var elements = value.getElementsByClassName(className);
if (elements && elements.length == 1) {
result = elements[0].innerHTML.unescapeHTML();
}
return result;
};
September 8, 2009 | Unregistered CommenterMartOn

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
All HTML will be escaped. Hyperlinks will be created for URLs automatically.