jump to navigation

These Chains of Selects October 29, 2008

Posted by reidmix in Code, Example, Javascript.
Tags: , , , , , ,

I was looking for javascript library that would manage select (HTML) elements that are dependent on one another.  These are the drop-downs you often see on online car sites where you select a make and it populates the next drop-down with the models for that car make.

I was hoping that there would be a libary that was publically available, object oriented, small, backed by JSON, and would even like it to be based on the Prototype library.  All I could find was a library that may satisfy one of my requirements but not all.  Libraries I found were either procedural, terribly bulky and complicated, and never backed by JSON.

In my buy it or build it moment, I decided to build it: here is a chained_selects javascript library.  It’s fairly lightweight, it runs in about 85 lines (comments and whitespace included), you can initialize it with javascript objects or JSON, it’s object oriented and relies on just a little bit of Prototype.

You need the heirarchy of data to back a ChainedSelect object, the form, and selects that are assoicated with each “level” in the heirarchy.  It can have as many levels you want in the hierarchy from 2 up.  So, in my car example, imagine you have a two car makes (Acura and Volkswagen) and the Acura make has two models (the RL and the TL, sweet!), and the Volkswagen make has three models (Beetle, Jetta, and the Passat).  Each of the models have a database id.  My JSON for this heirarchy would look like:

var data = {"Acura":{"RL":1, "TL":2},"Volkswagen":{"Beetle":3,"Jetta":4,"Passat":5}}

You can see the first level is the outer-most hash with the makes, and the inner mosts hashes have the models and their db ids.  For rails programmers, this could be a rails hash that you run to_json with.

I set up my HTML form, with a select for each level in the heirarchy, like so:

  <form id="cars" method="post" action="/choose_car">
    <select name="make"></select>
    <select name="model"></select>

Then I can setup the ChainedSelect object to manage these selects, using the data above:

new ChainedSelect(data, 'cars', ['make','model']);

This says, created a chained select with my data for the form with id ‘cars’, with the first level select with the name ‘make’ and second level select with the name ‘model’.

The first select will load with three elements:

  • Choose make…
  • Acura
  • Volkswagen

When Volkswagen is selected, the second select will load:

  • Choose model…
  • Beetle
  • Jetta
  • Passat

If Acura is then chose, the second select will refresh with the correct data. If the ‘model’ is chosen let’s say Jetta, then the id for that model is sent in the form (4).  For rails developers, the name of the select needs to be different from the label (“Choose model…”), you can specify a label and a select name separately, with an embedded array, like so:

new ChainedSelect(data, 'cars', ['make',['car[model]','model']]);

Oftentimes, we have an edit page with the values already prefilled on our forms, we can add another array parameter that specifies the path to the selection:

new ChainedSelect(data, 'cars', ['make','model'], ['Volkswagen','Jetta']);

Lastly, when the final select is chosen, sometimes we want to take an action or an AJAX call, we can supply an onComplete function to run which is yielded a the value chosen:

new ChainedSelect(data, 'cars', ['make','model'], null, function(choice) { alert("You chose:"+choice); } );

You can use all of these elements together to get the behavior you want, I also have a little rails form helper to create my javascript for me:

  def chained_selects(tree, form, selects, active, oncomplete=nil)
    %(new ChainedSelects(#{tree}, '#{form}', #{selects.inspect}, #{active.inspect}, #{oncomplete || 'null'});)

I know that the script can be improved in many ways (Doug McInnes added the onComplete feature).  Please feel free to send patches or any bugs you may find.



1. Nick Treffiletti - June 4, 2009

cool little plugin, Im getting an ‘ChainedSelect is not defined’ in firebug when testing out your examples. Once I change it to a ‘new ChainedSelect’ im getting a new error of ‘this.active is undefined’

my code is such:

//chained select js
new ChainedSelects({“Acura”:{“RL”:1, “TL”:2},”Volkswagen”:{“Beetle”:3,”Passat”:4}},’cars’, [[“make”, “Car Make”],”model”])
// end

2. Nick Treffiletti - June 4, 2009

loks like some of my code got cut off there, basically it was just the form in the example followed by what appears between the comments above in a body script tag

3. Nick Treffiletti - June 4, 2009

So it looks like you need to change all of your examples to be a new instance of ChainedSelects instead of ChainedSelect and you also need to populate the ‘active’ field in the function with an array to prevent getting ‘this.active is undefined’. Keeping the form markup the same the js here works…

new ChainedSelects({“Acura”:{“RL”:1, “TL”:2},”Volkswagen”:{“Beetle”:3,”Passat”:4}},’cars’, [“make”,”model”], [“make”])

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: