Comment puis-je trier une liste par ordre alphabétique à l'aide de jQuery?

233

Je suis un peu hors de ma profondeur ici et j'espère que c'est réellement possible.

J'aimerais pouvoir appeler une fonction qui trierait tous les éléments de ma liste par ordre alphabétique.

J'ai cherché dans l'interface jQuery pour le tri, mais cela ne semble pas être le cas. Des pensées?

Shog9
la source
Découvrez Underscore.js ou Sugar.js .
Kris Khaira

Réponses:

106

Vous n'avez pas besoin de jQuery pour ce faire ...

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

Facile à utiliser...

sortUnorderedList("ID_OF_LIST");

Démo en direct →

Josh Stodola
la source
30
Un problème que j'ai trouvé avec cette approche est que, puisque ce n'est que le texte qui est déplacé, si vous associez des données aux nœuds DOM à l'aide de jQuery.data avant le tri, ces associations pointent désormais vers les mauvais nœuds après le tri.
Rudism
13
Déplacer des éléments avec innerHTML est une mauvaise solution car ce ne sont pas les mêmes éléments après le tri. Toutes les références existantes aux éléments sont perdues. Tous les écouteurs d'événements liés à JavaScript sont perdus. Il serait préférable de stocker les éléments au lieu de innerHTML, d'utiliser une fonction de tri (vals.sort (fonction (a, b) {return b.innerHTML <a.innerHTML;})) et appendChild pour déplacer les éléments.
gregers
4
Buhuuu innerHTML! Ne l'utilisez pas. C'est du matériel Microsoft et n'a jamais été reconnu par le W3C.
Steve K
14
À mon humble avis, c'est une réponse horrible. Il est parfaitement possible de réorganiser les nœuds DOM sans les sérialiser et les désérialiser à nouveau, et sans détruire les propriétés et / ou événements attachés.
Alnitak
4
... mais si vous deviez utiliser jQuery, comment le feriez-vous?
Matthew
332

Quelque chose comme ça:

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

Depuis cette page: http://www.onemoretake.com/2009/02/25/sorting-elements-with-jquery/

Le code ci-dessus triera votre liste non ordonnée avec l'ID 'myUL'.

OU vous pouvez utiliser un plugin comme TinySort. https://github.com/Sjeiti/TinySort

SolutionYogi
la source
6
La dernière ligne peut-elle être remplacée par $ (listitems) .appendTo (mylist); ?
Amir
26
HL Menken a une citation qui décrit cette solution: "Pour chaque problème, il existe une solution simple, élégante et erronée." Ce processus s'exécute en temps O (n ^ 2). Ce n'est pas visible avec des listes relativement courtes, mais sur une liste contenant plus de 100 éléments, il faut 3-4 secondes pour terminer le tri.
Nathan Strong
5
@Nathan: À propos de "Pour chaque problème, il existe une solution simple, élégante et erronée." - eh bien, une mauvaise solution n'est pas élégante.
Johann Philipp Strathausen
18
Quelque chose peut être élégant et intrigant à regarder, mais échoue toujours. L'élégance n'implique pas le succès.
Jane Panda
13
Cette solution n'est pas fausse. Cela répond à la question. Le PO n'a pas précisé qu'il devait trier une liste de plus de 100 articles. Si sa liste ne dépassera jamais 100 éléments, cette solution est parfaitement acceptable. +1 pour signaler que la solution est lente, -1 pour déclarer une solution qui répond aux exigences comme «fausse».
Samurai Soul
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;    
}

démo en direct: http://jsbin.com/eculis/876/edit

Jeetendra Chauhan
la source
15
C'est la meilleure réponse. J'ai même comme dans une seule ligne comme ceci: $(".list li").sort(function(a, b){return ($(b).text()) < ($(a).text());}).appendTo('.list');. Une remarque cependant: .text()devrait être.text().toUpperCase()
Jules Colle
Malheureusement, cette solution ne fonctionne pas dans IE alors que la réponse de PatrickHecks ci-dessous fonctionne dans tous les navigateurs.
patg
8
Méfiez-vous du sélecteur! ".list li" sélectionnera toutes les balises LI descendantes, pas seulement les enfants immédiats.
Doug Domeny
3
@DougDomeny a raison. Mieux vaut appeler $(".list").children(), si possible, ou configurer le sélecteur avec une relation immédiate avec l'enfant comme$(".list > li")
mroncetwice
1
BTW Voici un lien vers un violon que j'ai créé en utilisant cette réponse. J'ai configuré le code comme une fonction tout-en-un: jsfiddle.net/mroncetwice/t0whh6fL
mroncetwice
39

Pour que cela fonctionne avec tous les navigateurs, y compris Chrome, vous devez faire en sorte que la fonction de rappel de sort () renvoie -1,0 ou 1.

voir 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
la source
2
Tous les éléments li ne doivent-ils pas être supprimés de ul.myList avant d'ajouter les éléments li triés?
Daud
2
@Daud, les éléments LI n'ont pas besoin d'être explicitement supprimés.
Doug Domeny
1
L'utilisation de .localeCompare serait une amélioration pour les caractères non ASCII.
Doug Domeny
1
@DougDomeny, pourquoi les éléments li n'ont-ils pas besoin d'être explicitement supprimés?
bowserm
3
@bowserm, la méthode appendTo déplace les éléments DOM plutôt que de les copier.
Doug Domeny
31

Si vous utilisez jQuery, vous pouvez le faire:

$(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>

Notez que renvoyer 1 et -1 (ou 0 et 1) à partir de la fonction de comparaison est absolument faux .

Salman A
la source
7

@ La réponse de SolutionYogi fonctionne comme un charme, mais il semble que l'utilisation de $ .each soit moins simple et efficace que l'ajout direct de listitems:

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);

Violon

Buzut
la source
$ ('# list'). empty () -> mylist.empty () serait mieux. Pas besoin de toucher à nouveau le DOM.
Jacob van Lingen
Absolument, je viens de le réparer!
Buzut
Cela ne fonctionne pas dans Internet Explorer (testé avec la version 10).
Chad Johnson
Je viens de tester avec IE11 et en fait, cela ne fonctionne pas. Mais le code de SolutionYogi ne fonctionnait pas non plus sous IE11… Est-ce que cela a fonctionné pour vous?
Buzut
1

amélioration basée sur la réponse de Jeetendra Chauhan

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

pourquoi je considère cela comme une amélioration:

  1. utilisation eachpour prendre en charge l'exécution sur plusieurs ul

  2. utiliser children('li')au lieu de ('ul li')est important parce que nous voulons uniquement traiter les enfants directs et non les descendants

  3. l'utilisation de la fonction flèche (a,b)=>semble juste meilleure (IE non pris en charge)

  4. en utilisant de la vanille innerTextau lieu de $(a).text()pour améliorer la vitesse

  5. l'utilisation de la vanille localeCompareaméliore la vitesse en cas d'éléments égaux (rare dans la vie réelle)

  6. utiliser appendTo(this)au lieu d'utiliser un autre sélecteur s'assurera que même si le sélecteur attrape plus d'un ul toujours rien ne casse

oriadam
la source
0

Je cherchais à le faire moi-même, et je n'étais satisfait d'aucune des réponses fournies simplement parce que, je pense, elles sont quadratiques, et je dois le faire sur des listes de centaines d'articles.

J'ai fini par étendre jquery, et ma solution utilise jquery, mais pourrait facilement être modifiée pour utiliser du javascript simple.

Je n'accède à chaque élément que deux fois et j'effectue un tri linéaire, donc je pense que cela devrait être beaucoup plus rapide sur les grands ensembles de données, bien que j'avoue librement que je peux me tromper:

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)));
    }
}
Quirkles
la source
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")

Démo JSFiddle https://jsfiddle.net/97oo61nw/

Ici, nous poussons toutes les valeurs des liéléments à l'intérieur ulavec spécifique id(que nous avons fourni comme argument de fonction) pour le tableau arret le trier en utilisant la méthode sort () qui est triée alphabétiquement par défaut. Une fois le tableau arrtrié, nous bouclons ce tableau en utilisant la méthode forEach () et nous remplaçons simplement le contenu textuel de tous les liéléments par du contenu trié

Mikhail
la source