Some time ago I wrote this:
function sortChildren(wrap, f, isNum) {
var l = wrap.children.length,
arr = new Array(l);
for(var i=0; i<l; ++i)
arr[i] = [f(wrap.children[i]), wrap.children[i]];
arr.sort(isNum
? function(a,b){ return a[0]-b[0]; }
: function(a,b){ return a[0]<b[0] ? -1 : a[0]>b[0] ? 1 : 0; }
);
var par = wrap.parentNode,
ref = wrap.nextSibling;
par.removeChild(wrap);
for(var i=0; i<l; ++i) wrap.appendChild(arr[i][1]);
par.insertBefore(wrap, ref);
}
Basically:
First create an array to store the elements with its corresponding value returned by the comparator function.
We could also run the function when sorting, but since DOM interactions are slow, this way we make sure the function will only run once per element.
Then, we sort it using native sort
.
If isNum
argument is truly, we use a sorting function that compares numerically. This is needed if the comparator function returns strings but you want to compare numerically instead of lexicographically.
Now, we remove the wrapper element from the DOM. This way reordering the children will be less expensive (e.g. avoiding repaitings).
Finally we reorder the children, and insert wrapper back in its place.
Run it like
sortChildren(
document.getElementById('list'),
function(li) { return li.dataset.index; },
true
);
or
sortChildren(
document.getElementById('list'),
function(li) { return +li.dataset.index; }
);
Demo