¿Cómo puedo ordenar una lista alfabéticamente usando jQuery?

233

Estoy un poco fuera de mi alcance aquí y espero que esto sea realmente posible.

Me gustaría poder llamar a una función que ordenaría alfabéticamente todos los elementos de mi lista.

He estado buscando en la interfaz de usuario jQuery para ordenar, pero eso no parece ser así. ¿Alguna idea?

Shog9
fuente
Echa un vistazo a Underscore.js o Sugar.js .
Kris Khaira

Respuestas:

106

Usted no necesita jQuery para hacer esto ...

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

Fácil de usar...

sortUnorderedList("ID_OF_LIST");

Demostración en vivo →

Josh Stodola
fuente
30
Un problema que encontré con este enfoque es que, dado que solo se está moviendo el texto, si asocia datos a los nodos DOM usando jQuery.data antes de la clasificación, esas asociaciones ahora apuntan a los nodos incorrectos después de la clasificación.
Rudism
13
Mover elementos con innerHTML es una mala solución porque no son los mismos elementos después de la ordenación. Todas las referencias existentes a los elementos se pierden. Todos los oyentes de eventos vinculados desde JavaScript se pierden. Sería mejor almacenar los elementos en lugar de innerHTML, usar una función de ordenación (vals.sort (function (a, b) {return b.innerHTML <a.innerHTML;})) y appendChild para mover elementos.
gregers
44
Buhuuu innerHTML! No uses eso. Es material de propiedad de Microsoft y nunca fue reconocido por el W3C.
Steve K
14
En mi humilde opinión, esta es una respuesta horrible. Es perfectamente posible reorganizar los nodos DOM sin serializarlos y deserializarlos nuevamente, y sin destruir ninguna propiedad y / o evento adjunto.
Alnitak
44
... pero si usaras jQuery, ¿cómo lo harías?
Mateo
332

Algo como esto:

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

Desde esta página: http://www.onemoretake.com/2009/02/25/sorting-elements-with-jquery/

El código anterior ordenará su lista desordenada con la identificación 'myUL'.

O puede usar un complemento como TinySort. https://github.com/Sjeiti/TinySort

SolutionYogi
fuente
66
¿Se puede reemplazar la última línea con $ (listitems) .appendTo (mylist); ?
Amir
26
HL Menken tiene una cita que describe esta solución: "Para cada problema, hay una solución que es simple, elegante e incorrecta". Este proceso se ejecuta en tiempo O (n ^ 2). No es notable con listas relativamente cortas, pero en una lista que contiene más de 100 elementos, lleva de 3 a 4 segundos terminar la clasificación.
Nathan Strong
55
@Nathan: Acerca de "Para cada problema, hay una solución que es simple, elegante e incorrecta". - Bueno, una solución incorrecta no es elegante.
Johann Philipp Strathausen
18
Algo puede ser elegante e intrigante de ver, pero aún así falla. La elegancia no implica éxito.
Jane Panda
13
Esta solución no está mal. Responde la pregunta. El OP no especificó que necesitaba ordenar una lista de más de 100 elementos. Si su lista nunca superará los 100 elementos, esta solución es perfectamente aceptable. +1 para señalar que la solución es lenta, -1 para declarar que una solución que cumple los requisitos es "incorrecta".
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;    
}

demostración en vivo: http://jsbin.com/eculis/876/edit

Jeetendra Chauhan
fuente
15
Esta es la mejor respuesta. Incluso me gusta que en una sola línea como esta: $(".list li").sort(function(a, b){return ($(b).text()) < ($(a).text());}).appendTo('.list');. Sin embargo, un comentario: .text()debería ser.text().toUpperCase()
Jules Colle
Desafortunadamente, esta solución no funciona en IE, mientras que la respuesta de PatrickHecks a continuación funciona en todos los navegadores.
patg
8
¡Cuidado con el selector! ".list li" seleccionará todas las etiquetas LI descendientes, no solo los elementos secundarios inmediatos.
Doug Domeny
3
@DougDomeny tiene razón. Es mejor llamar $(".list").children(), si es posible, o configurar el selector con una relación infantil inmediata como$(".list > li")
mroncetwice
1
Por cierto, aquí hay un enlace a un violín que hice usando esta respuesta. Configuré el código como una función todo en uno: jsfiddle.net/mroncetwice/t0whh6fL
mroncetwice
39

Para que este trabajo funcione con todos los navegadores, incluido Chrome, debe hacer que la función de devolución de llamada de sort () devuelva -1,0 o 1.

ver 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
fuente
2
¿No deberían eliminarse todos los elementos li de ul.myList antes de agregar los elementos li ordenados?
Daud
2
@Daud, los elementos LI no necesitan eliminarse explícitamente.
Doug Domeny
1
Usar .localeCompare sería una mejora para los caracteres que no son ASCII.
Doug Domeny 01 de
1
@DougDomeny, ¿por qué no es necesario eliminar explícitamente los elementos li?
bowserm
3
@bowserm, el método appendTo mueve los elementos DOM en lugar de copiarlos.
Doug Domeny
31

Si está utilizando jQuery, puede hacer esto:

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

Tenga en cuenta que devolver 1 y -1 (o 0 y 1) de la función de comparación es absolutamente incorrecto .

Salman A
fuente
7

@ Solución La respuesta de Yogi funciona de maravilla, pero parece que usar $ .each es menos sencillo y eficiente que agregar elementos de lista directamente:

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

Violín

Buzut
fuente
$ ('# list'). empty () -> mylist.empty () sería mejor. No es necesario tocar el DOM nuevamente.
Jacob van Lingen
¡Absolutamente, lo acabo de arreglar!
Buzut
Esto no funciona en Internet Explorer (probado con la versión 10).
Chad Johnson
Acabo de probar con IE11 y, de hecho, no funciona. Pero el código de SolutionYogi no funcionó ni bajo IE11 ... ¿Eso funcionó para usted?
Buzut
1

mejora basada en la respuesta de Jeetendra Chauhan

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

por qué lo considero una mejora:

  1. usando eachpara soportar correr en más de un ul

  2. usar en children('li')lugar de ('ul li')es importante porque solo queremos procesar hijos directos y no descendientes

  3. el uso de la función de flecha se (a,b)=>ve mejor (IE no es compatible)

  4. usando vainilla en innerTextlugar de $(a).text()para mejorar la velocidad

  5. el uso de vainilla localeComparemejora la velocidad en caso de elementos iguales (raro en el uso de la vida real)

  6. usar en appendTo(this)lugar de usar otro selector se asegurará de que incluso si el selector atrapa más de un ul todavía no se rompe nada

oriadam
fuente
0

Estaba buscando hacer esto yo mismo, y no estaba satisfecho con ninguna de las respuestas proporcionadas simplemente porque, creo, son de tiempo cuadrático, y necesito hacerlo en las listas de cientos de elementos.

Terminé extendiendo jquery, y mi solución usa jquery, pero podría modificarse fácilmente para usar JavaScript directo.

Solo accedo a cada elemento dos veces y realizo un tipo linealitmico, así que creo que esto debería funcionar mucho más rápido en grandes conjuntos de datos, aunque confieso libremente que podría estar equivocado allí:

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
fuente
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")

Demostración de JSFiddle https://jsfiddle.net/97oo61nw/

Aquí estamos empujando todos los valores de los lielementos dentro ulcon específicos id(que proporcionamos como argumento de función) para arrordenarlos y ordenarlos usando el método sort () que está ordenado alfabéticamente por defecto. Después de arrordenar la matriz, hacemos un bucle de esta matriz usando el método forEach () y simplemente reemplazamos el contenido de texto de todos los lielementos con contenido ordenado

Mikhail
fuente