2
\$\begingroup\$

On selection from drop-down A (HTML select) I need to populate another "sub"-dropdown with a different list of values. For instance, on selection of country in dropdown A, dropdown B should be filled with the list of cities of selected country.

This is my first attempt of writing such a thing in jQuery. Please point out areas for improvement, like more idiomatic or shorter way of achieving this result (without losing much readability if possible).

HTML:

<select id="Parentlist">
</select>

<select id="Sublist">
</select>

<table id="choices">
        <tr id="Poland" style="display:none">
            <td id="Krakow"></td>
            <td id="Warszawa"></td>
        </tr>
        <tr id="Germany" style="display:none">
            <td id="Frankfurt"></td>
            <td id="Berlin"></td>
        </tr>
</table>

JavaScript + jQuery:

function populateParentlist(choice) {
       $('#choices').find('tr').each(function() {
           trowid = $(this).attr('id');
           $("#Parentlist").append('<option>' +trowid + '</option>');
       });
}

function populateSublist(choice) {
    $('#Sublist').find('option').remove();
    $('#choices').find('tr').each(function() {
        trowid = $(this).attr('id');
        $(this).find('td').each(function() {
           tdid = $(this).attr('id');
            if(trowid != choice)
                return;            
           $('#Sublist').append('<option>' + tdid + '</option>');
        });
    });
}

populateParentlist('Poland')
populateSublist('Poland')

$(function() {
    $("#Parentlist").change(function() {
        var parsel = $(this).val();
        populateSublist(parsel);
    });
});

jsfiddle

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
  1. Don't use a <table> element to "hide" extra data in your markup. It's a terribly complex way of doing things, and it's pretty hacky to have large, hidden elements like that (and not a good semantic use of a table either).

  2. Be consistent with your naming; Sublist is capitalized, but choices is lowercase.

  3. Alphabetize the city names :)

With regard the JS: It's ok given the context, but the use of the table is the real culprit. Still there's at least 1 thing you should change:

Don't call the populate* before the page has loaded (you wait before adding the change handler, because the #ParentList element might not have loaded yet; that same timing issue would cause the populateParentlist function to fail)

Now, ideally, the city list should be available as JSON or something (loaded from the server for instance), but if you're going to store it in the HTML, you might as well use a select and subdivide it using optgroup elements:

<select id="cities">
  <optgroup label="Poland">
    <option selected>Krakow</option>
    <option>Warszawa</option>
  </optgroup>
  ...

As a bonus, the list will work even without javascript. And you can set the initially-selected city in the HTML.

It also contains all the option elements you'll need for the city dropdown; we just need to convert the optgroups into options for a 2nd dropdown:

$(function () { // wait for page to load
    var cityDropdown = $("#cities"),
        countryDropdown = $('<select></select>'), // create a country dropdown
        countries = []; // ordered list of countries

    // parse the nested dropdown
    cityDropdown.children().each(function () {
        var group = $(this),
            countryName = group.attr('label'),
            option;

        // create an option for the country
        option = $('<option></option>').text(countryName);

        // store the associated city options
        option.data('cities', group.children());

        // check if the country should be selected
        if( group.find(':selected').length > 0 ) {
            option.prop('selected', true);
        }

        // add the country to the dropdown
        countryDropdown.append(option);
    });

    // add the country dropdown to the page
    cityDropdown.before(countryDropdown);

    // this function updates the city dropdown based on the selected country
    function updateCities() {
        var country = countryDropdown.find(':selected');
        cityDropdown.empty().append(country.data('cities'));
    }

    // call the function to set the initial cities
    updateCities();

    // and add the change handler
    countryDropdown.on('change', updateCities);
});

Here's a demo

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.