2008-11-12 8 views
41

Si j'ai une liste comme ceci:Le moyen le plus simple de trier les nœuds 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> 

Quelle est la meilleure façon de réarranger les noeuds DOM à ma préférence? (Cela doit se faire automatiquement lorsque la page est chargée, la préférence d'ordre de liste est obtenue à partir d'un 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

Je ne sais pas si cela vous aidera, mais si vous utilisez une technologie côté serveur (comme PHP) sur ce page, alors il devrait également avoir accès aux cookies. en PHP, c'est dans le tableau global $ _COOKIE. – nickf

Répondre

62

Bien qu'il y ait probablement un moyen plus facile de le faire en utilisant une bibliothèque JS, voici une solution de travail en utilisant js vanille.

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

Merci pour votre solution NickF, je vais l'adapter un peu puisque j'utilise jQuery mais je pense avoir une idée de la façon de le faire maintenant, merci! :) – James

+27

En utilisant 'list.children' au lieu de' list.childNodes', vous pouvez éviter la recherche de nœuds de texte. –

+0

Quand j'ai lu cette réponse, j'ai pensé à la vanille js? Pourquoi [vanilla.js] (http://vanilla-js.com/) est-il requis ici? : 0) – zipzit

14

Vous pourriez constater que le tri des nœuds DOM ne fonctionne pas correctement. Une approche différente serait d'avoir dans votre javascript un tableau qui représente les données qui iraient dans les nœuds DOM, trier ces données, puis régénérer la div qui contient les nœuds DOM. Peut-être que vous n'avez pas autant de nœuds à trier, donc cela n'aurait pas d'importance. Mon expérience est basée sur essayer de trier les tables HTML en manipulant le DOM, y compris les tables avec des centaines de lignes et quelques dizaines de colonnes.

9

Si vous utilisez déjà jQuery, je vous recommande tinysort: http://tinysort.sjeiti.com/

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

Tinysort est plusieurs fois plus lent que 13 lignes de code, basé sur le tri de jquery résultat de la sélection et en l'ajoutant en arrière - http: // jsperf.com/attr-vs-getattribute/12 –

+0

TinySort était un plugin jQuery mais a été réécrit pour supprimer la dépendance jQuery. Il est maintenant plus petit et plus rapide –

1

sans analyser trop si cela apporte quelque chose de nouveau à la table, je l'habitude d'utiliser ceci:

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

où item est un tableau d'enfants du même parent. on enlève en commençant par le dernier et en ajoutant en commençant par le premier pour éviter de scintiller dans la partie supérieure qui est probablement à l'écran. je reçois habituellement mon tableau articles comme celui-ci:

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

Voir en action: 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); 
      }); 
     }; 
    })(); 

Terse

+0

Nice! J'aime aussi les choses laconiques, donc basé sur cela, je suis venu avec une [fonction jQuery très simple] (http://jsfiddle.net/mindplay/H2mrp/) qui vous permet de fournir une fonction de rappel qui calcule la valeur pour trier par. (J'ai inclus deux exemples: trier une liste par ordre alphabétique et trier une table par une valeur numérique dans la première colonne - les deux étant essentiellement des interlignes.) –

+0

Je sais que c'est un ancien article, mais puis-je demander, quel est l'avantage de en utilisant une fonction auto-exécutable retourner la fonction sortDomElements? Ne serait-ce pas plus laconique de dire jQuery.fn.sortDomElements = function (comparitor) ...? comme ceci http://jsfiddle.net/y7JyT/77/ – Julian

5

Ma version, l'espoir serait utile pour les autres:

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

Que voulez-vous dire '/ * sort logic * /'? :/Le code semble bon, mais ma logique de tri est un peu faible –

+1

Cela signifie que vous devriez mettre votre logique de tri là, jetez un oeil à https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/ Global_Objects/Array/le trie de la même manière, seuls x et y sont des noeuds DOM. –

2

Voici une fonction ES6 pour trier les nœuds DOM en place:

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

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

Voilà comment vous l'utiliser pour trier 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; 
    } 
}); 
Questions connexes