89

I'm trying to understand sorting option elements within a select element alphabetically. Ideally, I'd like to have this as a separate function where I can just pass in the select element since it needs to be sorted when the user clicks some buttons.

I've searched high and low for a good way of doing this, but haven't been able to find anything that worked for me.

The option elements should be sorted alphabetically by text, not value.

Is this possible in some way?

reformed
  • 4,505
  • 11
  • 62
  • 88
bomortensen
  • 3,346
  • 10
  • 53
  • 74
  • So basically you want to give a selector and order all of his children alphabetically by the text they contain ? – pkurek Aug 22 '12 at 12:37
  • I think you should view this topic: http://stackoverflow.com/questions/1134976/how-may-i-sort-a-list-alphabetically-using-jquery – kaz Aug 22 '12 at 12:37
  • @pkurek: In all it's simplicity, yes :-) kaz: Yeah, I've fooled around with that snippet, but simply can't get it to work :-/ Wondered if there were any other way to do it. – bomortensen Aug 22 '12 at 12:39
  • @bomortensen: All solutions will be essentially similar. [This answer](http://stackoverflow.com/a/1134983/218196) is as easy as it can get. – Felix Kling Aug 22 '12 at 12:56

11 Answers11

136

What I'd do is:

  1. Extract the text and value of each <option> into an array of objects;
  2. Sort the array;
  3. Update the <option> elements with the array contents in order.

To do that with jQuery, you could do this:

var options = $('select.whatever option');
var arr = options.map(function(_, o) { return { t: $(o).text(), v: o.value }; }).get();
arr.sort(function(o1, o2) { return o1.t > o2.t ? 1 : o1.t < o2.t ? -1 : 0; });
options.each(function(i, o) {
  o.value = arr[i].v;
  $(o).text(arr[i].t);
});

Here is a working jsfiddle.

edit — If you want to sort such that you ignore alphabetic case, you can use the JavaScript .toUpperCase() or .toLowerCase() functions before comparing:

arr.sort(function(o1, o2) {
  var t1 = o1.t.toLowerCase(), t2 = o2.t.toLowerCase();

  return t1 > t2 ? 1 : t1 < t2 ? -1 : 0;
});
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • This did it for me! Just had to make all the > and < checks to upper case :-) Thanks a lot!! – bomortensen Aug 22 '12 at 13:23
  • Ah OK right, if you want to ignore alphabetic case then yes. Glad it worked! – Pointy Aug 22 '12 at 13:28
  • yes, because JS weighs uppercase characters higher than lowercase when sorting.. so it seems ;-) Thanks again, works like a charm! – bomortensen Aug 22 '12 at 13:34
  • 7
    Thanks for this answer - solved my problem almost entirely. I just needed to augment it to account for tracking the selected option. For the arr options.map I added "s: $(o).attr('selected')", and then in options.each just needed to add: $(o).attr('selected', arr[i].s); Thanks! – alexleonard Jul 29 '13 at 07:41
  • I am new to jquery - where would I add the check for the upper case as mentioned by bomortensen? – user1261774 Oct 02 '14 at 01:49
  • @user1261774 if you want to sort without regard to alphabetic case, you can use some functions to convert the strings. I'll extend the answer. – Pointy Oct 02 '14 at 01:55
  • 3
    Here's the amended code to remember the currently selected option: http://jsfiddle.net/trELD/700/ – Jamie Barker Jul 20 '15 at 09:44
  • Seems to work without the get method. And how are you using that [get](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) without a key? – Geoffrey Hale Nov 03 '15 at 01:15
  • @GeoffreyHale it probably does work without `.get()`; the jQuery object more-or-less mimics an array. When you call `.get()` with no key, you get back a *real* array containing the DOM elements. – Pointy Nov 03 '15 at 04:01
  • may i know what is the meaning of passing a underscore in jquery .map() function – Slay Jan 28 '16 at 06:21
  • Why not use `value` and `text` as propertynames? Your minimizer will get rid of it :). Great answer though, thanks – Alexander Derck Aug 25 '16 at 13:54
69

Accepted answer is not the best in all cases because sometimes you want to perserve classes of options and different arguments (for example data-foo).

My solution is:

var sel = $('#select_id');
var selected = sel.val(); // cache selected value, before reordering
var opts_list = sel.find('option');
opts_list.sort(function(a, b) { return $(a).text() > $(b).text() ? 1 : -1; });
sel.html('').append(opts_list);
sel.val(selected); // set cached selected value

//For ie11 or those who get a blank options, replace html('') empty()

marxin
  • 3,692
  • 3
  • 31
  • 44
40

html:

<select id="list">
    <option value="op3">option 3</option>
    <option value="op1">option 1</option>
    <option value="op2">option 2</option>
</select>

jQuery:

var options = $("#list option");                    // Collect options         
options.detach().sort(function(a,b) {               // Detach from select, then Sort
    var at = $(a).text();
    var bt = $(b).text();         
    return (at > bt)?1:((at < bt)?-1:0);            // Tell the sort function how to order
});
options.appendTo("#list");                          // Re-attach to select

I used tracevipin's solution, which worked fantastically. I provide a slightly modified version here for anyone like me who likes to find easily readable code, and compress it after it's understood. I've also used .detach instead of .remove to preserve any bindings on the option DOM elements.

Gerbus
  • 2,554
  • 27
  • 22
32
<select id="mSelect" >
    <option value="val1" > DEF </option>
    <option value="val4" > GRT </option>
    <option value="val2" > ABC </option>
    <option value="val3" > OPL </option>
    <option value="val5" > AWS </option>
    <option value="val9" > BTY </option>
</select>

.

$("#mSelect").append($("#mSelect option").remove().sort(function(a, b) {
    var at = $(a).text(), bt = $(b).text();
    return (at > bt)?1:((at < bt)?-1:0);
}));
Diode
  • 24,570
  • 8
  • 40
  • 51
  • 3
    `$("#mSelect").append($options);` should suffice :) – Felix Kling Aug 22 '12 at 12:54
  • The callback to `.sort()` should return a number that's less than zero, zero, or greater, to reflect the ordering relationship. Returning a boolean won't really work right I don't think. – Pointy Aug 22 '12 at 13:29
  • 5
    I went with the above, changed to: var at = $(a).text().toLowerCase(), bt = $(b).text().toLowerCase(); to make sorting case independent – JayCrossler Jul 26 '14 at 19:11
8

I know this topic is old but I think my answer can be useful for a lot of people.

Here is jQuery plugin made from Pointy's answer using ES6:

/**
 * Sort values alphabetically in select
 * source: http://stackoverflow.com/questions/12073270/sorting-options-elements-alphabetically-using-jquery
 */
$.fn.extend({
    sortSelect() {
        let options = this.find("option"),
            arr = options.map(function(_, o) { return { t: $(o).text(), v: o.value }; }).get();

        arr.sort((o1, o2) => { // sort select
            let t1 = o1.t.toLowerCase(), 
                t2 = o2.t.toLowerCase();
            return t1 > t2 ? 1 : t1 < t2 ? -1 : 0;
        });

        options.each((i, o) => {
            o.value = arr[i].v;
            $(o).text(arr[i].t);
        });
    }
});

Use is very easy

$("select").sortSelect();
Kuxa
  • 187
  • 1
  • 9
7

Here's my improved version of Pointy's solution:

function sortSelectOptions(selector, skip_first) {
    var options = (skip_first) ? $(selector + ' option:not(:first)') : $(selector + ' option');
    var arr = options.map(function(_, o) { return { t: $(o).text(), v: o.value, s: $(o).prop('selected') }; }).get();
    arr.sort(function(o1, o2) {
      var t1 = o1.t.toLowerCase(), t2 = o2.t.toLowerCase();
      return t1 > t2 ? 1 : t1 < t2 ? -1 : 0;
    }); 
    options.each(function(i, o) {
        o.value = arr[i].v;
        $(o).text(arr[i].t);
        if (arr[i].s) {
            $(o).attr('selected', 'selected').prop('selected', true);
        } else {
            $(o).removeAttr('selected');
            $(o).prop('selected', false);
        }
    }); 
}

The function has the skip_first parameter, which is useful when you want to keep the first option on top, e.g. when it's "choose below:".

It also keeps track of the previously selected option.

Example usage:

jQuery(document).ready(function($) {

  sortSelectOptions('#select-id', true);

});
Community
  • 1
  • 1
Victor BV
  • 1,051
  • 13
  • 9
2

None of the answers worked for me. For some strange reason, when looping through the options, each option returns nothing when text() is called. Instead, I was forced to retrieve the option's label via attr('label')

/**
 * Sort the options of the target select list
 * alphabetically by label. For some reason, when
 * we call detach(), the returned options have no
 * text() and instead we're forced to get the option's
 * label via the 'label' attribute.
 * @param select jQuery selector
 */
function sort_multi_select(select) {
    var options = select.find('option');
    options.detach().sort(function (a, b) {
        var at = $(a).attr('label'), //label, not text()
            bt = $(b).attr('label');
        return at > bt ? 1 : at < bt ? -1 : 0;
    });
    options.appendTo(select);
}

//example
sort_multi_select($('#my_select'));
rmirabelle
  • 6,268
  • 7
  • 45
  • 42
2

I combined parts from marxin's and kuxa's excellent answers to create a jQuery custom function that

  1. sorts the options by their text values (case-insensitive),

  2. maintains any already-selected value, and

  3. returns the original jQuery objects against which the function is executed:

    $.fn.extend({
        sortSelect() {
            return this.each(function(){
                let $this = $(this),
                    original_selection = $this.val(),
                    $options = $this.find('option'),
                    arr = $options.map(function(_, o) { return { t: $(o).text(), v: o.value }; }).get();
    
                arr.sort((o1, o2) => {
                    // sort select
                    let t1 = o1.t.toLowerCase(), 
                        t2 = o2.t.toLowerCase();
                    return t1 > t2 ? 1 : t1 < t2 ? -1 : 0;
                });
    
                $options.each((i, o) => {
                    o.value = arr[i].v;
                    $(o).text(arr[i].t);
                });
    
                $this.val(original_selection); 
            })
        }
    });
    

Working example on jsFiddle is available at https://jsfiddle.net/jhfrench/64och25e/.

Jeromy French
  • 11,812
  • 19
  • 76
  • 129
  • I love that I could use some of the time this Q/A saved me by enhancing the answers in a way that hopefully benefits someone else. Viva Stack Overflow!!! – Jeromy French May 27 '20 at 18:12
  • Please note that this way doesn't preserve data-xxx="abc" and moves the option order to another one which may create risky cases – Erdinç Çorbacı Jan 03 '23 at 04:26
1

The jquery.selectboxes.js plugin has a sort method. You can implement the plugin, or dive into the code to see a way to sort the options.

Blackcoat77
  • 1,574
  • 1
  • 21
  • 31
Sam Tyson
  • 4,496
  • 4
  • 26
  • 34
1

Yes you can sort the options by its text and append it back to the select box.

 function NASort(a, b) {    
      if (a.innerHTML == 'NA') {
          return 1;   
      }
      else if (b.innerHTML == 'NA') {
          return -1;   
      }       
      return (a.innerHTML > b.innerHTML) ? 1 : -1;
  };

Fiddle: https://jsfiddle.net/vaishali_ravisankar/5zfohf6v/

shali ravi
  • 31
  • 1
1

Malakgeorge answer is nice an can be easily wrapped into a jQuery function:

$.fn.sortSelectByText = function(){
    this.each(function(){
        var selected = $(this).val(); 
        var opts_list = $(this).find('option');
        opts_list.sort(function(a, b) { return $(a).text() > $(b).text() ? 1 : -1; });
        $(this).html('').append(opts_list);
        $(this).val(selected); 
    })
    return this;        
}
Dr Fred
  • 939
  • 8
  • 15
  • It's a good solution but it doesn't work in IE11. When I applied this function to the dropdown list, the options' text gets empty. If you replace .html('') with .empty(), it should work. – Mukesh Kumar Dec 12 '20 at 02:27
  • Thank you for this comment ! – Dr Fred Oct 24 '21 at 17:23