在保留当前选定项的同时,按值对 Html 选择选项进行排序的最有效方法是什么?

我有 jQuery,但我不确定它是否有任何内置的排序助手。我可以为每个项目的 textvalueselected属性制作一个2d 数组,但是我不认为内置在 Array.sort()中的 javascript 可以正确工作。

100270 次浏览

Well, in IE6 it seems to sort on the nested array's [0] item:

function sortSelect(selectToSort) {
var arrOptions = [];


for (var i = 0; i < selectToSort.options.length; i++)  {
arrOptions[i] = [];
arrOptions[i][0] = selectToSort.options[i].value;
arrOptions[i][1] = selectToSort.options[i].text;
arrOptions[i][2] = selectToSort.options[i].selected;
}


arrOptions.sort();


for (var i = 0; i < selectToSort.options.length; i++)  {
selectToSort.options[i].value = arrOptions[i][0];
selectToSort.options[i].text = arrOptions[i][1];
selectToSort.options[i].selected = arrOptions[i][2];
}
}

I'll see if this works in other browsers...

Edit: it works in Firefox too, woo hoo!

Is there an easier way than this though? is there some method built into javascript or jQuery that sorts selects that I am missing, or is this the best way?

Array.sort() defaults to converting each element to a string, and comparing those values. So ["value", "text", "selected"] gets sorted as "value, text, selected". Which will probably work fine, most of the time.

If you do want to sort on value alone, or interpret value as a number, then you can pass a comparison function into sort():

arrOptions.sort(function(a,b) { return new Number(a[0]) - new Number(b[0]); });

There's a closed jQuery ticket for a sort that should work, but just wasn't included in the core.

jQuery.fn.sort = function() {
return this.pushStack( [].sort.apply( this, arguments ), []);
};

Referenced from a Google Groups thread, I think you just pass in a function that is used to sort, like so

function sortSelect(selectToSort) {
jQuery(selectToSort.options).sort(function(a,b){
return a.value > b.value ? 1 : -1;
});
}

Hope it helps!

Extract options into a temporary array, sort, then rebuild the list:

var my_options = $("#my_select option");
var selected = $("#my_select").val();


my_options.sort(function(a,b) {
if (a.text > b.text) return 1;
if (a.text < b.text) return -1;
return 0
})


$("#my_select").empty().append( my_options );
$("#my_select").val(selected);

Mozilla's sort documentation (specifically the compareFunction) and Wikipedia's Sorting Algorithm page are relevant.

If you want to make the sort case insensitive, replace text with text.toLowerCase()

The sort function shown above illustrates how to sort. Sorting non-english languages accurately can be complex (see the unicode collation algorithm). Using localeCompare in the sort function is a good solution, eg:

my_options.sort(function(a,b) {
return a.text.localeCompare(b.text);
});

I've just wrapped Mark's idea in a jquery function

$('#your_select_box').sort_select_box();

JQuery function:

$.fn.sort_select_box = function(){
var my_options = $("#" + this.attr('id') + ' option');
my_options.sort(function(a,b) {
if (a.text > b.text) return 1;
else if (a.text < b.text) return -1;
else return 0
})
return my_options;
}

Modified Tom's answer above slightly so that it actually modifies the contents of the select box to be sorted, rather than just returning the sorted elements.

$('#your_select_box').sort_select_box();

jQuery function:

$.fn.sort_select_box = function(){
// Get options from select box
var my_options = $("#" + this.attr('id') + ' option');
// sort alphabetically
my_options.sort(function(a,b) {
if (a.text > b.text) return 1;
else if (a.text < b.text) return -1;
else return 0
})
//replace with sorted my_options;
$(this).empty().append( my_options );


// clearing any selections
$("#"+this.attr('id')+" option").attr('selected', false);
}

Remember: if you want to use context selector, just concatenate the ID will not work

$.fn.sort_select_box = function(){
var my_options = $("option", $(this));
my_options.sort(function(a,b) {
if (a.text > b.text) return 1;
else if (a.text < b.text) return -1;
else return 0
});
$(this).empty().append(my_options);
}


// Usando:
$("select#ProdutoFornecedorId", $($context)).sort_select_box();

This is a better solution. Declare a global function to JQuery

$.fn.sortSelect = function() {
var op = this.children("option");
op.sort(function(a, b) {
return a.text > b.text ? 1 : -1;
})
return this.empty().append(op);
}

And call the function from the code.

$("#my_select").sortSelect();

The solution I mentioned in my comment to @Juan Perez

$.fn.sortOptions = function(){
$(this).each(function(){
var op = $(this).children("option");
op.sort(function(a, b) {
return a.text > b.text ? 1 : -1;
})
return $(this).empty().append(op);
});
}

Usage:

$("select").sortOptions();

This can still be improved on, but I didn't need to add any more bells or whistles :)

A bit late but for what it's worth I've implemented a more complex function you can include generically. It has a few options for varied output. It can also recurse into <OPTGROUP> tags based on their label.

$.fn.sortSelect = function(options){


const OPTIONS_DEFAULT = {
recursive:      true,   // Recurse into <optgroup>
reverse:        false,  // Reverse order
useValues:      false,  // Use values instead of text for <option> (<optgruop> is always label based)
blankFirst:     true,   // Force placeholder <option> with empty value first, ignores reverse
}


if (typeof options != "object" || null === options) {
options = OPTIONS_DEFAULT;
}


var sortOptions = function($root, $node, options){


if ($node.length != 1) {
return false;
}


if ($node[0].tagName != "SELECT" && $node[0].tagName != "OPTGROUP") {
return false;
}


if (options.recursive) {
$node.children('optgroup').each(function(k, v){
return sortOptions($root, $(v), options);
});
}


var $options        = $node.children('option, optgroup');
var $optionsSorted  = $options.sort(function(a, b){


if (options.blankFirst) {
if (a.tagName == "OPTION" && a.value == "") {
return -1;
}


if (b.tagName == "OPTION" && b.value == "") {
return 1;
}
}


var textA = (a.tagName == "OPTION" ? (options.useValues ? a.value : a.text) : a.label);
var textB = (b.tagName == "OPTION" ? (options.useValues ? a.value : b.text) : b.label);


if (textA > textB) {
return options.reverse ? -1 : 1;
}


if (textA < textB) {
return options.reverse ? 1 : -1;
}


return 0;


});


$options.remove();
$optionsSorted.appendTo($node);


return true;


};


var selected = $(this).val();
var sorted = sortOptions($(this), $(this), {...OPTIONS_DEFAULT, ...options});
$(this).val(selected);


return sorted;


};

You can then call the sortSelect() function on any <SELECT> tag, or just a single <OPTGROUP> to only sort a group's options.

Example:

$('select').sortSelect();

Reverse order using the "reverse" option:

$('select').sortSelect({
reverse: true
});

You could apply this to all selects automatically, perhaps only if they include an important class (e.g. "js-sort") with this:

$('select.js-sort').each(function(k, v){
$(v).sortSelect();
});

Seems jquery still is not particularly helpful enough for sorting options in a html select element. Here is some plain-plain javascript code for sorting options:

function sortOptionsByText(a,b) {
// I keep an empty value option on top, b.value comparison to 0 might not be necessary if empty value is always on top...
if (a.value.length==0 || (b.value.length>0 && a.text <= b.text)) return -1; // no sort: a, b
return 1; // sort switches places: b, a
}
function sortOptionsByValue(a,b) {
if (a.value <= b.value) return -1; // a, b
return 1; // b, a
}
function clearChildren(elem) {
if (elem) {
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
}
}
function sortSelectElem(sel,byText) {
const val=sel.value;
const tmp=[...sel.options];
tmp.sort(byText?sortOptionsByText:sortOptionsByValue);
clearChildren(sel);
sel.append(...tmp);
sel.value=val;
}
RACE: <select id="list" size="6">
<option value="">--PICK ONE--</option>
<option value="1">HUMANOID</option>
<option value="2">AMPHIBIAN</option>
<option value="3">REPTILE</option>
<option value="4">INSECTOID</option>
</select><br>
<button type="button" onclick="sortSelectElem(document.getElementById('list'));">SORT LIST BY VALUE</button><br>
<button type="button" onclick="sortSelectElem(document.getElementById('list'),true);">SORT LIST BY TEXT</button>

With jquery this worked for me in Chrome in order to sort a select made from database unsorted elements.

$(document).ready(function(){
    

var $list = $("#my_select");
var selected = $("#my_select").val(); //save selected value
    

$list.children().detach().sort(function(a, b) {
return $(a).text().localeCompare($(b).text());
}).appendTo($list);  //do the sorting locale for latin chars
    

$("#my_select").val(selected); //select previous selected value


});