Jak mogę posortować listę alfabetycznie za pomocą jQuery?

233

Jestem trochę z głębi i mam nadzieję, że to rzeczywiście możliwe.

Chciałbym móc wywołać funkcję, która sortowałaby wszystkie elementy na mojej liście alfabetycznie.

Przeglądałem interfejs użytkownika jQuery pod kątem sortowania, ale tak nie jest. jakieś pomysły?

Shog9
źródło
Sprawdź Underscore.js lub Sugar.js .
Kris Khaira

Odpowiedzi:

106

Zdajesz nie potrzeba jQuery to zrobić ...

function sortUnorderedList(ul, sortDescending) {
  if(typeof ul == "string")
    ul = document.getElementById(ul);

  // Idiot-proof, remove if you want
  if(!ul) {
    alert("The UL object is null!");
    return;
  }

  // Get the list items and setup an array for sorting
  var lis = ul.getElementsByTagName("LI");
  var vals = [];

  // Populate the array
  for(var i = 0, l = lis.length; i < l; i++)
    vals.push(lis[i].innerHTML);

  // Sort it
  vals.sort();

  // Sometimes you gotta DESC
  if(sortDescending)
    vals.reverse();

  // Change the list on the page
  for(var i = 0, l = lis.length; i < l; i++)
    lis[i].innerHTML = vals[i];
}

Łatwy w użyciu...

sortUnorderedList("ID_OF_LIST");

Demo na żywo →

Josh Stodola
źródło
29
Jednym z problemów, które znalazłem przy takim podejściu, jest to, że ponieważ przenoszony jest tylko tekst, jeśli skojarzysz dane z węzłami DOM za pomocą jQuery.data przed sortowaniem, skojarzenia te wskazują teraz niewłaściwe węzły po sortowaniu.
Rudyzm
13
Przenoszenie elementów za pomocą innerHTML jest złym rozwiązaniem, ponieważ po sortowaniu nie są to te same elementy. Wszystkie istniejące odniesienia do elementów zostaną utracone. Wszystkie detektory zdarzeń powiązane z JavaScript są tracone. Lepiej byłoby przechowywać elementy zamiast innerHTML, użyć funkcji sortowania (vals.sort (function (a, b) {return b.innerHTML <a.innerHTML;})) i appendChild do przenoszenia elementów.
gregers,
3
Buhuuu innerHTML! Nie używaj tego. Jest zastrzeżony przez Microsoft i nigdy nie został potwierdzony przez W3C.
Steve K
13
IMHO to okropna odpowiedź. Jest całkowicie możliwe przestawienie węzłów DOM bez szeregowania ich i ponownego testowania na nowo, bez niszczenia dołączonych właściwości i / lub zdarzeń.
Alnitak,
3
... ale gdybyś używał jQuery, jak byś to zrobił?
Matthew
332

Coś takiego:

var mylist = $('#myUL');
var listitems = mylist.children('li').get();
listitems.sort(function(a, b) {
   return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
})
$.each(listitems, function(idx, itm) { mylist.append(itm); });

Z tej strony: http://www.onemoretake.com/2009/02/25/sorting-elements-with-jquery/

Powyższy kod posortuje Twoją nieuporządkowaną listę o identyfikatorze „myUL”.

LUB możesz użyć wtyczki takiej jak TinySort. https://github.com/Sjeiti/TinySort

SolutionYogi
źródło
6
Czy ostatni wiersz można zastąpić $ (listitems) .appendTo (mylist); ?
Amir
26
HL Menken ma cytat opisujący to rozwiązanie: „Na każdy problem istnieje rozwiązanie, które jest proste, eleganckie i złe”. Ten proces przebiega w czasie O (n ^ 2). Nie jest to zauważalne w przypadku stosunkowo krótkich list, ale na liście zawierającej ponad 100 elementów zakończenie sortowania zajmuje 3-4 sekundy.
Nathan Strong
5
@Nathan: O „Dla każdego problemu istnieje rozwiązanie, które jest proste, eleganckie i złe”. - cóż, złe rozwiązanie nie jest eleganckie.
Johann Philipp Strathausen
18
Coś może być eleganckie i intrygujące do oglądania, ale nadal nie działa. Elegancja nie oznacza sukcesu.
Jane Panda,
13
To rozwiązanie nie jest złe. Odpowiada na pytanie. OP nie określił, że musi posortować listę ponad 100 pozycji. Jeśli jego lista nigdy nie będzie dłuższa niż 100 pozycji, to rozwiązanie jest całkowicie do przyjęcia. +1 za wskazanie, że rozwiązanie jest wolne, -1 za zadeklarowanie rozwiązania spełniającego wymagania jako „niewłaściwe”.
Dusza Samuraja
94
$(".list li").sort(asc_sort).appendTo('.list');
//$("#debug").text("Output:");
// accending sort
function asc_sort(a, b){
    return ($(b).text()) < ($(a).text()) ? 1 : -1;    
}

// decending sort
function dec_sort(a, b){
    return ($(b).text()) > ($(a).text()) ? 1 : -1;    
}

demo na żywo: http://jsbin.com/eculis/876/edit

Jeetendra Chauhan
źródło
15
To najlepsza odpowiedź. I nawet jak to w jednej linii tak: $(".list li").sort(function(a, b){return ($(b).text()) < ($(a).text());}).appendTo('.list');. Jedna uwaga: .text()powinien być.text().toUpperCase()
Jules Colle
Niestety to rozwiązanie nie działa w IE, podczas gdy PatrickHecks odpowiedź poniżej działa we wszystkich przeglądarkach.
patg
8
Uwaga na selektor! „.list li” wybierze wszystkie potomne znaczniki LI, a nie tylko bezpośrednie dzieci.
Doug Domeny,
3
@DougDomeny ma rację. Lepiej zadzwonić $(".list").children(), jeśli to możliwe, lub skonfigurować selektor z bezpośrednią relacją dziecka, np.$(".list > li")
mroncetwice,
1
BTW Oto link do skrzypce, które zrobiłem przy użyciu tej odpowiedzi. Skonfigurowałem kod jako funkcję „wszystko w jednym”: jsfiddle.net/mroncetwice/t0whh6fL
mroncetwice
39

Aby działało to we wszystkich przeglądarkach, w tym w Chrome, musisz ustawić funkcję zwrotną sort () zwracającą -1,0 lub 1.

patrz http://inderpreetsingh.com/2010/12/01/chromes-javascript-sort-array-function-is-different-yet-proper/

function sortUL(selector) {
    $(selector).children("li").sort(function(a, b) {
        var upA = $(a).text().toUpperCase();
        var upB = $(b).text().toUpperCase();
        return (upA < upB) ? -1 : (upA > upB) ? 1 : 0;
    }).appendTo(selector);
}
sortUL("ul.mylist");
PatrickHeck
źródło
2
Czy nie należy usunąć wszystkich elementów li z ul.myList przed dołączeniem posortowanych elementów li?
Daud
2
@Daud, elementów LI nie trzeba jawnie usuwać.
Doug Domeny,
1
Użycie .localeCompare byłoby ulepszeniem dla znaków spoza ASCII.
Doug Domeny,
1
@DougDomeny, dlaczego elementy li nie muszą być wyraźnie usuwane?
bowserm
3
@bowserm, metoda appendTo przenosi elementy DOM zamiast je kopiować.
Doug Domeny,
31

Jeśli używasz jQuery, możesz to zrobić:

$(function() {

  var $list = $("#list");

  $list.children().detach().sort(function(a, b) {
    return $(a).text().localeCompare($(b).text());
  }).appendTo($list);

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<ul id="list">
  <li>delta</li>
  <li>cat</li>
  <li>alpha</li>
  <li>cat</li>
  <li>beta</li>
  <li>gamma</li>
  <li>gamma</li>
  <li>alpha</li>
  <li>cat</li>
  <li>delta</li>
  <li>bat</li>
  <li>cat</li>
</ul>

Zauważ, że zwracanie 1 i -1 (lub 0 i 1) z funkcji porównania jest absolutnie niepoprawne .

Salman A.
źródło
7

@ Rozwiązanie Odpowiedź Yogi działa jak urok, ale wydaje się, że użycie $ .each jest mniej proste i wydajne niż bezpośrednie dołączanie listitemów:

var mylist = $('#list');
var listitems = mylist.children('li').get();

listitems.sort(function(a, b) {
   return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase());
})

mylist.empty().append(listitems);

Skrzypce

Buzut
źródło
$ ('# lista'). empty () -> mylist.empty () byłoby lepsze. Nie trzeba ponownie dotykać DOM.
Jacob van Lingen,
Oczywiście, właśnie to naprawiłem!
Buzut
To nie działa w przeglądarce Internet Explorer (testowanej z wersją 10).
Chad Johnson,
Właśnie przetestowałem z IE11 i tak naprawdę to nie działa. Ale kod SolutionYogi nie działał również pod IE11… Czy to działało dla ciebie?
Buzut,
1

Poprawa na podstawie Jeetendra Chauhan odpowiedź „s

$('ul.menu').each(function(){
    $(this).children('li').sort((a,b)=>a.innerText.localeCompare(b.innerText)).appendTo(this);
});

dlaczego uważam to za ulepszenie:

  1. za pomocą eachdo obsługi działającej na więcej niż jednej ul

  2. używanie children('li')zamiast ('ul li')jest ważne, ponieważ chcemy przetwarzać tylko bezpośrednie dzieci, a nie potomków

  3. użycie funkcji strzałki (a,b)=>wygląda po prostu lepiej (IE nie jest obsługiwane)

  4. używając wanilii innerTextzamiast $(a).text()przyspieszenia

  5. używanie wanilii localeComparepoprawia prędkość w przypadku równych elementów (rzadkie w prawdziwym życiu)

  6. użycie appendTo(this)zamiast użycia innego selektora sprawi, że nawet jeśli selektor złapie więcej niż jedną ul, nadal nic się nie zepsuje

oriadam
źródło
0

Chciałem to zrobić sam i nie byłem usatysfakcjonowany żadną z udzielonych odpowiedzi po prostu dlatego, że, jak sądzę, są to czasy kwadratowe i muszę to zrobić na listach o długości setek pozycji.

Skończyło się na rozszerzeniu jquery, a moje rozwiązanie używa jquery, ale można je łatwo zmodyfikować, aby używało prostego javascript.

Uzyskuję dostęp do każdego elementu tylko dwa razy i wykonuję jeden sort liniowy, więc myślę, że powinno to działać znacznie szybciej w przypadku dużych zestawów danych, choć swobodnie przyznaję, że mogłem się tutaj pomylić:

sortList: function() {
   if (!this.is("ul") || !this.length)
      return
   else {
      var getData = function(ul) {
         var lis     = ul.find('li'),
             liData  = {
               liTexts : []
            }; 

         for(var i = 0; i<lis.length; i++){
             var key              = $(lis[i]).text().trim().toLowerCase().replace(/\s/g, ""),
             attrs                = lis[i].attributes;
             liData[key]          = {},
             liData[key]['attrs'] = {},
             liData[key]['html']  = $(lis[i]).html();

             liData.liTexts.push(key);

             for (var j = 0; j < attrs.length; j++) {
                liData[key]['attrs'][attrs[j].nodeName] = attrs[j].nodeValue;
             }
          }

          return liData;
       },

       processData = function (obj){
          var sortedTexts = obj.liTexts.sort(),
              htmlStr     = '';

          for(var i = 0; i < sortedTexts.length; i++){
             var attrsStr   = '',
                 attributes = obj[sortedTexts[i]].attrs;

             for(attr in attributes){
                var str = attr + "=\'" + attributes[attr] + "\' ";
                attrsStr += str;
             }

             htmlStr += "<li "+ attrsStr + ">" + obj[sortedTexts[i]].html+"</li>";
          }

          return htmlStr;

       };

       this.html(processData(getData(this)));
    }
}
Dziwactwa
źródło
0

HTML

<ul id="list">
    <li>alpha</li>
    <li>gamma</li>
    <li>beta</li>
</ul>

JavaScript

function sort(ul) {
    var ul = document.getElementById(ul)
    var liArr = ul.children
    var arr = new Array()
    for (var i = 0; i < liArr.length; i++) {
        arr.push(liArr[i].textContent)
    }
    arr.sort()
    arr.forEach(function(content, index) {
        liArr[index].textContent = content
    })
}

sort("list")

JSFiddle Demo https://jsfiddle.net/97oo61nw/

Tutaj wypychamy wszystkie wartości lielementów za ulpomocą określonych id(które podano jako argument funkcji) do tablicy arri sortujemy za pomocą metody sort () , która jest domyślnie sortowana alfabetycznie. Po arrposortowaniu tablicy zapętlamy tę tablicę za pomocą metody forEach () i po prostu zastępujemy zawartość tekstową wszystkich lielementów posortowaną treścią

Michaił
źródło