abbisDK
abbisDK

Reputation: 35

Sort dropdown list

I am trying to sort a dropdownlist (selector) in html.

I have two lists, a pre-selection which then should be used to define the content of the secont list.

A short code sample:

<!doctype html>
<html class="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

</head>

<body>
<table width=100% border="0">
  <tr>
  <td>&nbsp;</td>
    <td>Product</td>
    <td><select name="product" id="product" width="300" style="width: 300px">
      <option></option>
      <option id="TLX">TLX</option>
      <option id="FLX">FLX</option>
      <option id="MLX">MLX</option>
    </select></td>
    <td>&nbsp;</td>
  </tr>
  <tr>
  <td>&nbsp;</td>
    <td>Power</td>
    <td><select name="power" id="power" width="300" style="width: 300px">
      <option></option>
      <option name="TLX">6</option>
      <option name="TLX">8</option>
      <option name="TLX">10</option>
      <option name="TLX">12</option>
      <option name="TLX">15</option>
      <option name="FLX">6</option>
      <option name="FLX">8</option>
      <option name="FLX">10</option>
      <option name="FLX">12</option>
      <option name="FLX">15</option>
      <option name="FLX">17</option>
      <option name="MLX">30</option>
      <option name="MLX">40</option>
      <option name="MLX">60</option>
    </select></td>
    <td>&nbsp;</td>
  </tr>
</table>

</body>
</html>

I want to b able to select the product and then must the power list be sortet accordingly.

For instance if TLX is choosen then only 6, 8, 10, 12, and 15 are shown as possibilities in the second list.

Upvotes: 0

Views: 30632

Answers (4)

dc5
dc5

Reputation: 12441

No attempt was made to be cross browser here… But there's only one browser I can think of that might give you trouble :)

Demo

Code

var product = document.getElementById('product');
var power   = document.getElementById('power');
var allOpts = power.getElementsByTagName('option');
var opts = {
    empty: allOpts[0]
};

// This builds three additional lists of options for each type of product
// It relies on having a single attribute - if more are added, this logic
// would need to be adjusted.
for(var i = 1; i < allOpts.length; ++i) {
    var name = allOpts[i].attributes[0].value;

    opts[name] = opts[name] || [];
    opts[name].push(allOpts[i]);
}

// Each time product is changed, just loop through based on the selected ID
// and add those options to the power select list (removing all children first)
product.addEventListener('change', function(evt) {
    var val = evt.target.value;

    power.innerHTML = '';
    power.appendChild(opts.empty);
    for(var i = 0; i < opts[val].length; ++i) {
        power.appendChild(opts[val][i]);
    }
});

Here is a version using documentFragment:

Demo

documentFragment version

var product = document.getElementById('product');
var power   = document.getElementById('power');
var allOpts = power.getElementsByTagName('option');
var opts = {
    empty: allOpts[0]
};

// Appending to the doc fragment removes it from the live nodelist
while(allOpts.length > 1) {
    var name = allOpts[1].attributes[0].value;

    opts[name] = opts[name] || document.createDocumentFragment();
    opts[name].appendChild(allOpts[1]);
}

product.addEventListener('change', function(evt) {
    var val = evt.target.value;

    power.innerHTML = '';
    power.appendChild(opts.empty);
    power.appendChild(opts[val].cloneNode(true));
});

Update

With just a little more work, this can be made more generic - say when new requirements come in comments to the original answer :)

Additional requirements

Note: I added the empty option element to keep the solution simpler. The code can be adapted to handle the absence of it as well, but that will be left as an exercise for the reader.

<select name="continent" id="continent">'
    <option value="IMAGES/maps/europe_gray.png" id="EU">Europe</option>
    <option value="IMAGES/maps/europe_gray.png" id="AS">Asia</option>
</select>
<select name="country" id="country">
    <option></option>
    <option name="EU" id="ALB" value="IMAGES/flags/al.png">Albania, Republic of</option>
    <option name="AS" id="RUS" value="IMAGES/flags/ru.png">Russia</option>
</select>

With the changes below, the controller logic is now more generic in that it can adapt to the markup by accepting additional information that tells it where to find its keys and select elements.

Generic Controller Example

/* params {
 *    master: id of the master select element
 *    subord: id of the subordinate select element
 *    mKey:   name of the attribute holding the key in master that will be used to filter subordinate options
 *    sKey:   name of the attribute in subordinate that will be used to match against mKey
 */
function selectController(params) {
    var master = document.getElementById(params.master);
    var subord = document.getElementById(params.subord);
    var allOpts = subord.getElementsByTagName('option');
    var opts = {
        empty: allOpts[0]
    };

    // Appending to the doc fragment removes it from the live nodelist
    while (allOpts.length > 1) {
        var name = allOpts[1].getAttribute(params.sKey);

        opts[name] = opts[name] || document.createDocumentFragment();
        opts[name].appendChild(allOpts[1]);
    }

    master.addEventListener('change', function (evt) {
        var sel  = master.options[master.selectedIndex];
        var mKey = sel.getAttribute(params.mKey); 

        subord.innerHTML = '';
        subord.appendChild(opts.empty);
        subord.appendChild(opts[mKey].cloneNode(true));
    });
}

selectController({
    master: 'product',
    subord: 'power',
    mKey:   'id',
    sKey:   'name'
});

selectController({
    master: 'continent',
    subord: 'country',
    mKey:   'id',
    sKey:   'name'
});

Upvotes: 3

Luan Castro
Luan Castro

Reputation: 1184

using vanilla you can do like below

see the demo working http://jsfiddle.net/ST5dq/

no use name attribute, use data-name, value or other attribute, the name attribute not working cross browser to option tag...

i'll use data-name

<table width=100% border="0">
  <tr>
  <td>&nbsp;</td>
    <td>Product</td>
    <td><select name="product" id="product" width="300" style="width: 300px">
      <option></option>
      <option id="TLX">TLX</option>
      <option id="FLX">FLX</option>
      <option id="MLX">MLX</option>
    </select></td>
    <td>&nbsp;</td>
  </tr>
  <tr>
  <td>&nbsp;</td>
    <td>Power</td>
    <td><select name="power" id="power" width="300" style="width: 300px">
      <option></option>
      <option data-name="TLX">6</option>
      <option data-name="TLX">8</option>
      <option data-name="TLX">10</option>
      <option data-name="TLX">12</option>
      <option data-name="TLX">15</option>
      <option data-name="FLX">6</option>
      <option data-name="FLX">8</option>
      <option data-name="FLX">10</option>
      <option data-name="FLX">12</option>
      <option data-name="FLX">15</option>
      <option data-name="FLX">17</option>
      <option data-name="MLX">30</option>
      <option data-name="MLX">40</option>
      <option data-name="MLX">60</option>
    </select></td>
    <td>&nbsp;</td>
  </tr>
</table>

you can use this to sort the list

function sortDropdownList(ddl){

    var options = [].slice.apply(ddl.options, [0]);
    ddl.innerHTML = "";
    var sorted = options.sort(function(a,b){     
       return +(a.innerText) - +(b.innerText);
    });

    for(var i = 0; i < sorted.length; i++){
      ddl.options.add(sorted[i]);
    }  

}

you need pass a dom element, not a jQuery element.

to bind the select parent to select child you can do this

on you page initialize add the code

var parentSelect = document.getElementById("product");
var childSelect = document.getElementById("power");
var options = [].slice.apply(childSelect, [0]);
var emptyOption = options[0];
childSelect.innerHTML = "";

parentSelect.addEventListener("change", function(e){

    var selectedId = parentSelect.options[parentSelect.selectedIndex].id;
    childSelect.innerHTML = "";
    childSelect.options.add(emptyOption);
    for(var i = 0; i < options.length; i++){

        if( options[i].getAttribute("data-name") == selectedId ){

           childSelect.options.add(options[i]);

        }
    }

    sortDropdownList(childSelect);

});

Upvotes: 2

user2668376
user2668376

Reputation: 3240

If you are using jquery, can be accomplished like so: Demo

$(document).ready(function(){    
    var powerOptions = $('#power option').hide();
    $('#product').change(function(){
        powerOptions.hide().filter('option[name=' + $(this).find('option:selected').val() + ']').show();
    });
});

Note: In product dropdown, change id attribute of each option to name.

Upvotes: 1

Elvin Mammadov
Elvin Mammadov

Reputation: 27387

You need cascading dropdownlist

Please look at this links:

And you can find other links about cascading dropdownlists from google search or other...

Upvotes: 0

Related Questions