What is the most efficient way to sort an Html Select's Options by value, while preserving the currently selected item?
JavascriptJqueryArraysSortingHtml SelectJavascript Problem Overview
I have jQuery but I'm not sure if it has any built-in sorting helpers. I could make a 2d array of each item's text
, value
, and selected
properties, but I don't think that javascript's built in Array.sort()
would work correctly.
Javascript Solutions
Solution 1 - Javascript
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][1] (specifically the compareFunction) and [Wikipedia's Sorting Algorithm page][2] are relevant.
[1]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort "Mozilla's sort documentation" [2]: http://en.wikipedia.org/wiki/Sorting_algorithm
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);
});
Solution 2 - Javascript
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);
}
Solution 3 - Javascript
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;
}
Solution 4 - Javascript
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 :)
Solution 5 - Javascript
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?
Solution 6 - Javascript
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!
Solution 7 - Javascript
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();
Solution 8 - Javascript
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]); });
Solution 9 - Javascript
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();
Solution 10 - Javascript
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();
});
Solution 11 - Javascript
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>
Solution 12 - Javascript
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
});