Javascript to sort contents of select element

Viewed 135826

is there a quick way to sort the items of a select element? Or I have to resort to writing javascript?

Please any ideas.

<select size="4" name="lstALL" multiple="multiple" id="lstALL" tabindex="12" style="font-size:XX-Small;height:95%;width:100%;">
<option value="0"> XXX</option>
<option value="1203">ABC</option>
<option value="1013">MMM</option>
</select>
22 Answers

This will do the trick. Just pass it your select element a la: document.getElementById('lstALL') when you need your list sorted.

function sortSelect(selElem) {
    var tmpAry = new Array();
    for (var i=0;i<selElem.options.length;i++) {
        tmpAry[i] = new Array();
        tmpAry[i][0] = selElem.options[i].text;
        tmpAry[i][1] = selElem.options[i].value;
    }
    tmpAry.sort();
    while (selElem.options.length > 0) {
        selElem.options[0] = null;
    }
    for (var i=0;i<tmpAry.length;i++) {
        var op = new Option(tmpAry[i][0], tmpAry[i][1]);
        selElem.options[i] = op;
    }
    return;
}

Vanilla JS es6 Localization Options Sorting Example

const optionNodes = Array.from(selectNode.children);
const comparator = new Intl.Collator(lang.slice(0, 2)).compare;

optionNodes.sort((a, b) => comparator(a.textContent, b.textContent));
optionNodes.forEach((option) => selectNode.appendChild(option));

My use case was to localize a country select dropdown with locale aware sorting. This was used on 250 + options and was very performant ~10ms on my machine.

From the W3C FAQ:

Although many programming languages have devices like drop-down boxes that have the capability of sorting a list of items before displaying them as part of their functionality, the HTML <select> function has no such capabilities. It lists the <options> in the order received.

You'd have to sort them by hand for a static HTML document, or resort to Javascript or some other programmatic sort for a dynamic document.

I had the same problem. Here's the jQuery solution I came up with:

  var options = jQuery.makeArray(optionElements).
                       sort(function(a,b) {
                         return (a.innerHTML > b.innerHTML) ? 1 : -1;
                       });
  selectElement.html(options);

Yes DOK has the right answer ... either pre-sort the results before you write the HTML (assuming it's dynamic and you are responsible for the output), or you write javascript.

The Javascript Sort method will be your friend here. Simply pull the values out of the select list, then sort it, and put them back :-)

Try this...hopefully it will offer you a solution:

function sortlist_name()
{

    var lb = document.getElementById('mylist');
    arrTexts = new Array();
    newTexts = new Array();
    txt = new Array();
    newArray =new Array();
    for(i=0; i<lb.length; i++)
    {
      arrTexts[i] = lb.options[i].text;
    }
    for(i=0;i<arrTexts.length; i++)
    {
        str = arrTexts[i].split(" -> ");
        newTexts[i] = str[1]+' -> '+str[0];
    }
    newTexts.sort();
    for(i=0;i<newTexts.length; i++)
    {
        txt = newTexts[i].split(' -> ');
        newArray[i] = txt[1]+' -> '+txt[0];
    }
    for(i=0; i<lb.length; i++)
    {
        lb.options[i].text = newArray[i];
        lb.options[i].value = newArray[i];
    }
}
/***********revrse by name******/
function sortreverse_name()
{

    var lb = document.getElementById('mylist');
    arrTexts = new Array();
    newTexts = new Array();
    txt = new Array();
    newArray =new Array();
    for(i=0; i<lb.length; i++)
    {
      arrTexts[i] = lb.options[i].text;
    }
    for(i=0;i<arrTexts.length; i++)
    {
        str = arrTexts[i].split(" -> ");
        newTexts[i] = str[1]+' -> '+str[0];
    }
    newTexts.reverse();
    for(i=0;i<newTexts.length; i++)
    {
        txt = newTexts[i].split(' -> ');
        newArray[i] = txt[1]+' -> '+txt[0];
    }
    for(i=0; i<lb.length; i++)
    {
        lb.options[i].text = newArray[i];
        lb.options[i].value = newArray[i];
    }
}

function sortlist_id() {
var lb = document.getElementById('mylist');
arrTexts = new Array();

for(i=0; i<lb.length; i++)  {
  arrTexts[i] = lb.options[i].text;
}

arrTexts.sort();

for(i=0; i<lb.length; i++)  {
  lb.options[i].text = arrTexts[i];
  lb.options[i].value = arrTexts[i];
}
}

/***********revrse by id******/
function sortreverse_id() {
var lb = document.getElementById('mylist');
arrTexts = new Array();

for(i=0; i<lb.length; i++)  {
  arrTexts[i] = lb.options[i].text;
}

arrTexts.reverse();

for(i=0; i<lb.length; i++)  {
  lb.options[i].text = arrTexts[i];
  lb.options[i].value = arrTexts[i];
}
}
</script>



  ID<a href="javascript:sortlist_id()"> &#x25B2;  </a> <a href="javascript:sortreverse_id()">&#x25BC;</a> |  Name<a href="javascript:sortlist_name()"> &#x25B2;  </a> <a href="javascript:sortreverse_name()">&#x25BC;</a><br/>

<select name=mylist id=mylist size=8 style='width:150px'>

<option value="bill">4 -> Bill</option>
<option value="carl">5 -> Carl</option>
<option value="Anton">1 -> Anton</option>
<option value="mike">2 -> Mike</option>
<option value="peter">3 -> Peter</option>
</select>
<br>

For those who are looking to sort whether or not there are optgroup :

/**
 * Sorting options 
 * and optgroups
 * 
 * @param selElem select element
 * @param optionBeforeGroup ?bool if null ignores, if true option appear before group else option appear after group
 */
function sortSelect(selElem, optionBeforeGroup = null) {
    let initialValue = selElem.tagName === "SELECT" ? selElem.value : null; 
    let allChildrens = Array.prototype.slice.call(selElem.childNodes);
    let childrens = [];

    for (let i = 0; i < allChildrens.length; i++) {
        if (allChildrens[i].parentNode === selElem && ["OPTGROUP", "OPTION"].includes(allChildrens[i].tagName||"")) {
            if (allChildrens[i].tagName == "OPTGROUP") {
                sortSelect(allChildrens[i]);
            }
            childrens.push(allChildrens[i]);
        }
    }

    childrens.sort(function(a, b){
        let x = a.tagName == "OPTGROUP" ? a.getAttribute("label") : a.innerHTML;
        let y = b.tagName == "OPTGROUP" ? b.getAttribute("label") : b.innerHTML;
        x = typeof x === "undefined" || x === null ? "" : (x+"");
        y = typeof y === "undefined" || y === null ? "" : (y+"");

        if (optionBeforeGroup === null) {
            if (x.toLowerCase().trim() < y.toLowerCase().trim()) {return -1;}
            if (x.toLowerCase().trim() > y.toLowerCase().trim()) {return 1;}
        } else if (optionBeforeGroup === true) {
            if ((a.tagName == "OPTION" && b.tagName == "OPTGROUP") || x.toLowerCase().trim() < y.toLowerCase().trim()) {return -1;}
            if ((a.tagName == "OPTGROUP" && b.tagName == "OPTION") || x.toLowerCase().trim() > y.toLowerCase().trim()) {return 1;}
        } else if (optionBeforeGroup === false) {
            if ((a.tagName == "OPTGROUP" && b.tagName == "OPTION") || x.toLowerCase().trim() < y.toLowerCase().trim()) {return -1;}
            if ((a.tagName == "OPTION" && b.tagName == "OPTGROUP") || x.toLowerCase().trim() > y.toLowerCase().trim()) {return 1;}
        }
        return 0;
    });

    if (optionBeforeGroup !== null) {
        childrens.sort(function(a, b){
            if (optionBeforeGroup === true) {
                if (a.tagName == "OPTION" && b.tagName == "OPTGROUP") {return -1;}
                if (a.tagName == "OPTGROUP" && b.tagName == "OPTION") {return 1;}
            } else {
                if (a.tagName == "OPTGROUP" && b.tagName == "OPTION") {return -1;}
                if (a.tagName == "OPTION" && b.tagName == "OPTGROUP") {return 1;}
            }
            return 0;
        });
    }

    selElem.innerHTML = "";
    for (let i = 0; i < childrens.length; i++) {
        selElem.appendChild(childrens[i]);
    }

    if (selElem.tagName === "SELECT") {
        selElem.value = initialValue;
    }
}

I think my function is more general for strings or numbers and does not sort the first element if it could mean All.

/** Check if a string can be parsed as a number. */
function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0) };

/** Sort options of HTML elements. */
function sortOptions(selectElement, exceptFirstOpt=false) {

    // List of options.
    var options = selectElement.options;
    // If empty list, do nothing.
    if(!options || options.length==0) return;

    // Array.
    var optionsArray = [];
    for (var i = (exceptFirstOpt ? 1 : 0); i < options.length; i++)
        optionsArray.push(options[i]);
    // Sort.
    optionsArray = optionsArray.sort(function (a, b) {      
        let v1 = a.innerHTML.toLowerCase();
        let v2 = b.innerHTML.toLowerCase();
        if((v1==undefined || v1 == '') && (v2==undefined || v2 == ''))
            return 0;
        else if(v1==undefined || v1.trim() == '') return 1;
        else if(v2==undefined || v2.trim() == '') return -1;

        // If number.
        if(isNumber(v1) && isNumber(v2))
            return parseFloat(v1)>parseFloat(v2);

        return v1.localeCompare(v2);   
    });

    // Update options.
    for (var i = 0; i <= optionsArray.length; i++)            
        options[i + (exceptFirstOpt ? 1 : 0)] = optionsArray[i];
    // First option selected by default.
    options[0].selected = true;
}
let selectOrDatalist = document.querySelector('#sdl');
/* optional added option
   selectOrDatalist.insertAdjacentHTML('afterbegin', `<option id="${id}" value="${foo}">${bar}</option>` );
*/
selectOrDatalist.append(...[...selectOrDatalist.options].sort((a,b) => a.value.localeCompare(b.value)));
Related