1
\$\begingroup\$

This is based (somewhat loosely) on the code written in this railscast. I'm planning to re-use it in different places throughout the site, so I made it a bit more generic rather than based on IDs. However, I'm certain there's probably room for improvement.

jQuery ->
 $('.org_type_choice').each ->
 cat_obj = $(this).find('.org_category')
 type_obj = $(this).find('.org_type')
 categories = cat_obj.html()
 org_type = type_obj.find(':selected').text()
 opts = $(categories).filter("optgroup[label='#{org_type}']").html()
 cat_obj.html(opts)
 type_obj.change ->
 org_type = $(this).find(':selected').text()
 opts = $(categories).filter("optgroup[label='#{org_type}']").html()
 cat_obj.html(opts)
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Feb 27, 2013 at 2:21
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

I see a couple of small ways to improve this.

  1. DRY (Don't Repeat Yourself) the code. The code that runs on load is identical to the change event handler. So make it a function you can call from both places.

  2. Decouple it completely from the markup by dropping the "org-*" classes. It may not be "orgs" with "categories" and "types" that you're trying to select, but, say, "cars" with "makes" and "models". The basic idea is simply that there's a primary select element, and it scopes (filters) a secondary select element.

For the second point, I'd use a custom data-* attribute on the primary select. You might call it data-secondary, and let its value be the ID of the secondary select element. E.g.

<select data-secondary="categories">...</select>
<select id="categories">...</select>

Now you can find all select elements that have an associated secondary select element, by saying $("select[data-secondary]"). From there, you can find that secondary, and use the current logic. The markup declares the relationship and behavior, while the code can stay completely generic.

I end up with code like this:

jQuery ->
 $("select[data-secondary]").each ->
 primary = $ this
 secondary = $ "##{primary.data 'secondary'}" # find the secondary
 items = secondary.clone() # clone the secondary into memory
 updateSecondarySelect = ->
 scope = primary.find(":selected").text()
 secondary.html items.find("optgroup[label='#{scope}']").html()
 primary.on "change", updateSecondarySelect # set up the event listener
 updateSecondarySelect() # do the initial filtering

(I've changed to CamelCase, but that's not important; use whatever style you prefer)

Some extra error checking would be good (e.g. what happens if the secondary isn't found? If there's no optgroup matching the selected option in the primary, should the secondary maybe just be hidden? etc).

Here's a demo

answered Feb 27, 2013 at 3:18
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.