Drew Noakes
Drew Noakes

Reputation: 310967

JavaScript to sort DOM elements by some comparator, without jQuery

Imagine some DOM elements:

<ul id="list">
  <li data-index="3">Baz</li>
  <li data-index="1">Foo</li>
  <li data-index="2">Bar</li>
</ul>

How can these elements be sorted using JavaScript and without jQuery?

Something similar to:

document.getElementById('list').sort(function(li) { return li.dataset.index; });

Upvotes: 6

Views: 4481

Answers (3)

user663031
user663031

Reputation:

You should use the ordering capabilities of flexboxes. This will allow to re-order the elements without moving them around in the DOM. This involves setting the CSS order property.

See https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes for more details.

Upvotes: 5

Oriol
Oriol

Reputation: 288250

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:

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

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

  3. Now, we remove the wrapper element from the DOM. This way reordering the children will be less expensive (e.g. avoiding repaitings).

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

Upvotes: 3

kennebec
kennebec

Reputation: 104780

<!doctype html>
<html lang="en">
<head>
<meta charset= "utf-8">
<title>sort list</title>
</head>
<body>
<strong>Double click the list to sort by data-index</strong>
<ul id= "list">
<li data-index= "3">Baz</li>
<li data-index= "1">Foo</li>
<li data-index= "2">Bar</li>
</ul>
<script>

document.body.ondblclick=function(){
    var A=[], pa= document.getElementById('list');
    var itemz= pa.getElementsByTagName('li');

    A.slice.call(itemz).sort(function(a, b){
        return a.getAttribute('data-index')-b.getAttribute('data-index');
    }).forEach(function(next){
            pa.appendChild(next);
    });
}

</script>
</body>
</html>

Upvotes: 1

Related Questions