Jaki jest najbardziej efektywny sposób sortowania opcji HTML Select według wartości, przy jednoczesnym zachowaniu aktualnie wybranej pozycji?

81

Mam jQuery, ale nie jestem pewien, czy ma wbudowane pomocniki sortowania. Mogłem zrobić 2d tablicy każdego elementu text, valueoraz selectedwłaściwości, ale nie sądzę, że JavaScript jest wbudowany Array.sort()będzie działać prawidłowo.

Travis
źródło
4
Notatka dla siebie (ponieważ wielokrotnie wracałem do tego pytania przez Google): oto dobre rozwiązanie, które napisałeś: gist.github.com/1072537
travis

Odpowiedzi:

135

Wyodrębnij opcje do tymczasowej tablicy, posortuj, a następnie odbuduj 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);

Dokumentacja sortowania Mozilli (w szczególności funkcja porównania) i strona Algorytm sortowania Wikipedii są istotne.

Jeśli chcesz, aby przypadek sortowania nieczuły, wymienić textztext.toLowerCase()

Funkcja sortowania pokazana powyżej ilustruje sposób sortowania. Dokładne sortowanie języków innych niż angielski może być skomplikowane (zobacz algorytm sortowania Unicode ). Użycie localeCompare w funkcji sort jest dobrym rozwiązaniem, np .:

my_options.sort(function(a,b) {
    return a.text.localeCompare(b.text);
});
znak
źródło
1
wracając do tego pytania 3 lata później, i ta trasa wydaje się być właściwą drogą
travis
8
Zwróć uwagę, że rozróżniana jest wielkość liter - to znaczy, że posortuje wszystkie rzeczy, które zaczynają się od wielkich liter, przed rzeczami, które zaczynają się od małych liter. Jeśli jest to niepożądane, po prostu dodaj .toUpperCase()po każdym.text
TJ Ellis
10
Ponadto nie zachowa aktualnie wybranej opcji we wszystkich przeglądarkach bez zmian (Chrome zapamiętał, co zostało wybrane, ale firefox i tzn. Nie, i wybrał ostatni element po wczytaniu opcji). Aby to naprawić, zapisz bieżącą wybraną wartość przed sortowaniem, a następnie wybierz ją ponownie. tj. zanim cokolwiek innego zrobi, var selected = $("#my_select").val(); a następnie po dołączeniu posortowanych opcji, przywróć wartość val, tj.$("#my_select").val(selected);
TJ Ellis
3
fajne. Słowa „else” można usunąć. powrót i tak kończy funkcję.
EliSherer
@TJEllis jeśli chodzi o rozróżnianie wielkości liter, to pytanie dostarcza jeszcze więcej informacji, dlaczego należy używać .toUpperCase()vs .toLowerCase()ignorując przypadki stackoverflow.com/questions/2140627/…
Adrien Be
20

Zmodyfikowano nieco powyższą odpowiedź Toma, aby faktycznie modyfikowała zawartość pola wyboru do posortowania, a nie tylko zwracała posortowane elementy.

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

Funkcja jQuery:

$.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);
}
JaredC
źródło
1
Ta odpowiedź jest bliska góry, ale nie została wybrana poprawnie
thouliha
18

Właśnie zawarłem pomysł Marka w funkcji jQuery

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

Funkcja JQuery:

$.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;
}
Tom Maeckelberghe
źródło
jak mogę dodać to do mojego obecnego zdarzenia kliknięcia? $ ('# addwire'). click (function () {return! $ ('# wireelection option: selected'). remove (). appendTo ('# cables'). sort_select_box ();}); nie wydaje się działać
mcgrailm
12

Rozwiązanie, o którym wspomniałem w komentarzu do @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);
    });
}

Stosowanie:

$("select").sortOptions();

Nadal można to poprawić, ale nie musiałem dodawać więcej dzwonków ani gwizdków :)

Tom Pietrosanti
źródło
6

Cóż, w IE6 wydaje się, że sortuje według pozycji [0] zagnieżdżonej tablicy:

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];
    }
}

Zobaczę, czy to zadziała w innych przeglądarkach ...

Edycja: działa też w Firefoksie, woo hoo!

Czy jest jednak łatwiejszy sposób niż ten? czy jest jakaś metoda wbudowana w javascript lub jQuery, która sortuje elementy, których mi brakuje, czy jest to najlepszy sposób?

Travis
źródło
8
Zauważ, że ta funkcja nie sortuje elementów opcji ; zmienia [wartość, tekst, zaznaczone] na istniejących elementach tak, że są sortowane (w tym przypadku według wartości). Jest to wystarczające dla większości aplikacji, ale jeśli masz inne atrybuty (CSS, podpowiedzi itp.), To takie podejście nie wystarczy.
NVRAM
6

Istnieje zamknięty bilet jQuery dla rodzaju, który powinien działać, ale po prostu nie został uwzględniony w rdzeniu.

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

Odwołując się do wątku Grup dyskusyjnych Google , myślę, że po prostu przekazujesz funkcję, która służy do sortowania, tak jak to

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

Mam nadzieję, że to pomoże!

bdukes
źródło
1
Niektóre problemy dotyczące funkcji sortSelect: Nie dotyczy to równych opcji. Sortuje według wartości, a nie tekstu. Obiekty jQuery nie mają właściwości o nazwie „opcje”. Nie ma żadnego efektu, ponieważ musisz dodać opcje do nowego elementu select.
simon
4

To lepsze rozwiązanie. Zadeklaruj funkcję globalną w 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);
}

I wywołaj funkcję z kodu.

$("#my_select").sortSelect();
Juan Perez
źródło
1
Użyłem wariacji tego, zawijając wewnętrzną część w $ (this) .each (), aby móc przekazać mu selektor, aby obsłużyć więcej niż jeden naraz.
Tom Pietrosanti,
1
Ponadto musisz użyć $(this)zamiast tylkothis
Tom Pietrosanti
2

Array.sort()domyślnie konwertuje każdy element na ciąg i porównuje te wartości. Więc ["value", "text", "selected"]jest sortowany jako "value, text, selected". Co prawdopodobnie będzie działać dobrze przez większość czasu.

Jeśli chcesz sortować według samej wartości lub interpretować wartość jako liczbę, możesz przekazać funkcję porównującą do sort ():

arrOptions.sort(function(a,b) { return new Number(a[0]) - new Number(b[0]); });
Shog9
źródło
1

Pamiętaj: jeśli chcesz użyć selektora kontekstu, po prostu konkatenacja ID nie zadziała

$.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();
João
źródło
1

Trochę późno, ale warto zaimplementować bardziej złożoną funkcję, którą można ogólnie uwzględnić. Ma kilka opcji zróżnicowanych wyników. Może również powtarzać się w <OPTGROUP>tagach na podstawie ich etykiety.

$.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;

};

Następnie możesz wywołać sortSelect()funkcję na dowolnym <SELECT>tagu lub tylko pojedynczym, <OPTGROUP>aby posortować tylko opcje grupy.

Przykład:

$('select').sortSelect();

Odwróć kolejność za pomocą opcji „odwróć”:

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

Możesz to zastosować do wszystkich selekcji automatycznie, być może tylko wtedy, gdy zawierają ważną klasę (np. „Js-sort”) z tym:

$('select.js-sort').each(function(k, v){
    $(v).sortSelect();
});
Adambean
źródło