Easiest way to sort DOM nodes?

JavascriptDom

Javascript Problem Overview


If I have a list like this:

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

What's the easiest way to re-arrange the DOM nodes to my preference? (This needs to happen automatically when the page loads, the list-order preference is gained from a 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>

Javascript Solutions


Solution 1 - Javascript

Though there's probably an easier way to do this using a JS Library, here's a working solution using vanilla 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]);
}

Solution 2 - Javascript

Use es6 syntax to resort children:

var list = document.querySelector('#test-list');

[...list.children]
  .sort((a,b)=>a.innerText>b.innerText?1:-1)
  .forEach(node=>list.appendChild(node));

Solution 3 - Javascript

You might find that sorting the DOM nodes doesn't perform well. A different approach would be to have in your javascript an array that represents the data that would go into the DOM nodes, sort that data, and then regenerate the div that holds the DOM nodes.

Maybe you dont' have that many nodes to sort, so it wouldn't matter. My experience is based on trying to sort HTML tables by manipulating the DOM, including tables with hundreds of rows and a couple dozen columns.

Solution 4 - Javascript

See it in 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

Solution 5 - Javascript

My version, hope will be useful for others:

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

Solution 6 - Javascript

If you're already using jQuery, I'd recommend tinysort : http://tinysort.sjeiti.com/

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

Solution 7 - Javascript

Here's an ES6 function to sort DOM nodes in place:

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

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

Here's how you would use it to sort 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;
  }
});

Solution 8 - Javascript

without analyzing too much if this brings anything new to the table, i usually use this:

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

where item is an array of children of the same parent. we remove starting with the last and append starting with the first to avoid flickering in the top part which is probably on screen. i usually get my items array like this:

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

Solution 9 - Javascript

The neatest way I can think of:

The param compare is just like the compare function used in Array.sort().

Sort child nodes.

/**
 * @param {!Node} parent
 * @param {function(!Node, !Node):number} compare
 */
function sortChildNodes(parent, compare) {
  const moveNode = (newParent, node) => {
    // If node is already under a parent, append() removes it from the
    // original parent before appending it to the new parent.
    newParent.append(node);
    return newParent;
  };
  parent.append(Array.from(parent.childNodes) // Shallow copies of nodes.
                    .sort(compare) // Sort the shallow copies.
                    .reduce(moveNode, document.createDocumentFragment()));
}

Sort child elements (a subset of child nodes).

/**
 * @param {!Element} parent
 * @param {function(!Element, !Element):number} compare
 */
function sortChildren(parent, compare) {
  const moveElement = (newParent, element) => {
    // If element is already under a parent, append() removes it from the
    // original parent before appending it to the new parent.
    newParent.append(element);
    return newParent;
  };
  parent.append(Array.from(parent.children) // Shallow copies of elements.
                    .sort(compare) // Sort the shallow copies.
                    .reduce(moveElement, document.createDocumentFragment()));
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJamesView Question on Stackoverflow
Solution 1 - JavascriptnickfView Answer on Stackoverflow
Solution 2 - JavascriptahuigoView Answer on Stackoverflow
Solution 3 - JavascriptCorey TragerView Answer on Stackoverflow
Solution 4 - JavascriptMars RobertsonView Answer on Stackoverflow
Solution 5 - JavascriptEbrahim ByagowiView Answer on Stackoverflow
Solution 6 - JavascriptJayView Answer on Stackoverflow
Solution 7 - JavascriptcgencoView Answer on Stackoverflow
Solution 8 - JavascriptpeterView Answer on Stackoverflow
Solution 9 - JavascriptLeedehaiView Answer on Stackoverflow