2008-11-12 18 views
41

Si tengo una lista como esta:¿La manera más fácil de clasificar los nodos DOM?

<ul id="mylist"> 
    <li id="list-item1">text 1</li> 
    <li id="list-item2">text 2</li> 
    <li id="list-item3">text 3</li> 
    <li id="list-item4">text 4</li> 
</ul> 

¿Cuál es la forma más fácil de volver a organizar los nodos DOM a mi preferencia? (Esto debe suceder automáticamente cuando se carga la página, la preferencia de orden de lista se obtiene de una cookie)

E.g.

<ul id="mylist"> 
    <li id="list-item3">text 3</li> 
    <li id="list-item4">text 4</li> 
    <li id="list-item2">text 2</li> 
    <li id="list-item1">text 1</li> 
</ul> 
+0

No estoy seguro de si esto le ayudará, pero si usted está utilizando una tecnología del lado del servidor (como PHP) en este página, entonces también debería tener acceso a las cookies. en PHP, está en la matriz global $ _COOKIE. – nickf

Respuesta

62

Aunque es probable que haya una manera más fácil de hacer esto utilizando una biblioteca de JS, aquí es una solución de trabajo utilizando vainilla js.

var list = document.getElementById('mylist'); 

var items = list.childNodes; 
var itemsArr = []; 
for (var i in items) { 
    if (items[i].nodeType == 1) { // get rid of the whitespace text nodes 
     itemsArr.push(items[i]); 
    } 
} 

itemsArr.sort(function(a, b) { 
    return a.innerHTML == b.innerHTML 
      ? 0 
      : (a.innerHTML > b.innerHTML ? 1 : -1); 
}); 

for (i = 0; i < itemsArr.length; ++i) { 
    list.appendChild(itemsArr[i]); 
} 
+0

Gracias por su solución NickF, voy a adaptarlo un poco ya que estoy usando jQuery, pero creo que tengo una idea de cómo hacerlo ahora, ¡gracias! :) – James

+27

Al usar 'list.children' en lugar de' list.childNodes', puede evitar el control de nodos de texto. –

+0

Cuando leí esta respuesta por primera vez, pensé en vanilla js? ¿Por qué se requiere [vanilla.js] (http://vanilla-js.com/) aquí? : ^) – zipzit

14

Es posible que la ordenación de los nodos DOM no tenga un buen rendimiento. Un enfoque diferente sería tener en su javascript una matriz que represente los datos que entrarían en los nodos DOM, ordenarlos y luego regenerar el div que contiene los nodos DOM.

Quizás no tengas tantos nodos para ordenar, por lo que no importaría. Mi experiencia se basa en intentar ordenar tablas HTML manipulando el DOM, incluidas las tablas con cientos de filas y un par de docenas de columnas.

9

Si ya está usando jQuery, me gustaría recomendar tinysort: http://tinysort.sjeiti.com/

$("li").tsort({order:"asc"}); 
$("li").tsort({order:"desc"}); 
+2

Tinysort es varias veces más lento que 13 líneas de código, según la clasificación del resultado de selección de jquery y su anexión - http: // jsperf.com/attr-vs-getattribute/12 –

+0

TinySort solía ser un complemento de jQuery pero se reescribió para eliminar la dependencia de jQuery. Ahora es más pequeño y más rápido –

1

sin analizar demasiado si esto trae nada nuevo a la mesa, que suelen utilizar esto:

function forEach(ar, func){ if(ar){for(var i=ar.length; i--;){ func(ar[i], i); }} } 
function removeElement(node){ return node.parentNode.removeChild(node); } 
function insertBefore(ref){ return function(node){ return ref.parentNode.insertBefore(node, ref); }; } 

function sort(items, greater){ 
    var marker = insertBefore(items[0])(document.createElement("div")); //in case there is stuff before/after the sortees 
    forEach(items, removeElement); 
    items.sort(greater); 
    items.reverse(); //because the last will be first when reappending 
    forEach(items, insertBefore(marker)); 
    removeElement(marker); 
} 

donde item es una matriz de hijos del mismo padre. removemos empezando por el último y añadimos el que comienza con el primero para evitar el parpadeo en la parte superior, que probablemente esté en la pantalla. Por lo general obtener mi artículo en su conjunto como esto:

forEachSnapshot(document.evaluate(..., 6, null), function(n, i){ items[i] = n; }); 
13

verlo en acción: http://jsfiddle.net/stefek99/y7JyT/

jQuery.fn.sortDomElements = (function() { 
     return function(comparator) { 
      return Array.prototype.sort.call(this, comparator).each(function(i) { 
        this.parentNode.appendChild(this); 
      }); 
     }; 
    })(); 

Conciso

+0

¡Agradable! También me gustan las cosas claras, así que basándome en eso, se me ocurrió una [función jQuery muy simple] (http://jsfiddle.net/mindplay/H2mrp/) que te permite proporcionar una función de devolución de llamada que calcula el valor para ordenar por. (Incluí dos ejemplos: ordenando una lista alfabéticamente y ordenando una tabla por un valor numérico en la primera columna, ambos esencialmente de una sola línea.) –

+0

Sé que esta es una publicación anterior, pero ¿puedo preguntar cuál es el beneficio de utilizando una función autoejecutable, ¿devuelve la función sortDomElements? ¿No sería más escueto decir jQuery.fn.sortDomElements = function (comparitor) ...? como este http://jsfiddle.net/y7JyT/77/ – Julian

5

Mi versión, la esperanza sería útil para los demás:

var p = document.getElementById('mylist'); 
Array.prototype.slice.call(p.children) 
    .map(function (x) { return p.removeChild(x); }) 
    .sort(function (x, y) { return /* sort logic */; }) 
    .forEach(function (x) { p.appendChild(x); }); 
+0

¿Qué quiere decir '/ * sort logic * /'? :/El código se ve bien, pero mi lógica de ordenación es un poco débil –

+1

Significa que debe poner su lógica de ordenación allí, eche un vistazo a https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/ Global_Objects/Array/sort it básicamente igual, solo x e y son nodos DOM. –

2

Aquí hay una función ES6 para ordenar los nodos DOM en su lugar:

const sortChildren = ({ container, childSelector, getScore }) => { 
    const items = [...container.querySelectorAll(childSelector)]; 

    items 
    .sort((a, b) => getScore(b) - getScore(a)) 
    .forEach(item => container.appendChild(item)); 
}; 

Así es como usted lo utilizaría para ordenar Untapped user reviews by score:

sortChildren({ 
    container: document.querySelector("#main-stream"), 
    childSelector: ".item", 
    getScore: item => { 
    const rating = item.querySelector(".rating"); 
    if (!rating) return 0; 
    const scoreString = [...rating.classList].find(c => /r\d+/.test(c)); 
    const score = parseInt(scoreString.slice(1)); 
    return score; 
    } 
}); 
Cuestiones relacionadas