Sort elements by document order in JavaScript
Asked Answered
E

2

7

I have an array of Elements and I want them in document order. I know this is trivial to achieve in XPath, but the logic I have to implement is a bit complicated for a single expression.

I did find Node.compareDocumentPosition(), but it generates a bit mask of quite a few combinations, so not very ideal for a comparator.

As I final resort, I could probably add a random attribute on to all the elements in the array and select them again using XPath, but I'd rather not do that if possible.

Eliaeliades answered 13/8, 2015 at 14:34 Comment(3)
how is the array built? is it a posability to build the array in document order intially rather then sort it after the event?Salian
@Salian The elements are the results of some existing function calls. I can theoretically select them in document order, but the code won't look any better than the hacks I mentioned and adds unnecessary maintenance burden.Eliaeliades
Just thinking aloud, maybe a brute force approach to reselect the elements from the document? Depending on how you could determine an accurate selector for each (and only) element, you could run down the list and construct a selector to grab them all. In JQuery that might result in something like this: $('#id20, #id5, #id12, #id7')... JQuery will return the set in document order, not in the order specified in the selector.Superstar
C
18

I don't necessarily agree that document.compareDocumentPosition() is insufficient for a comparator. Why do you consider this not ideal?

var elementArray = [];

elementArray.push(document.getElementById('div3'));
elementArray.push(document.getElementById('div2'));
elementArray.push(document.getElementById('div4'));
elementArray.push(document.getElementById('div1'));

function documentPositionComparator (a, b) {
  if (a === b) {
    return 0;
  }

  var position = a.compareDocumentPosition(b);

  if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
    return -1;
  } else if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
    return 1;
  } else {
    return 0;
  }

}

console.log('unsorted::', elementArray);
console.log('sorted::', elementArray.sort(documentPositionComparator));
<div id="div1">Div 1
  <div id="div2">Div 2
    <div id="div3">Div 3</div>
    <div id="div4">Div 4</div>
  </div>
</div>
Catanddog answered 13/8, 2015 at 15:7 Comment(1)
Okay. The code does not look as horrible as I thought it would be.Eliaeliades
D
0

Provided the elements have unique identifiers, querySelectorAll returns results in document order.

function findInDomOrder(elements) {
    const selector = elements.map(x => '#' + x.id).join(',')
    return document.querySelectorAll(selector)
}

const elements = [
    document.getElementById('d3'),
    document.getElementById('d2'),
    document.getElementById('d4'),
    document.getElementById('d1'),
]

console.log(findInDomOrder(elements))

// [Log] NodeList (4)
// 0 
// <div id="d1"></div>
// 1 
// <div id="d2"></div>
// 2 
// <div id="d3"></div>
// 3
// <div id="d4"></div>


Davin answered 2/7 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.