sjngm
sjngm

Reputation: 12871

jQuery: Order Inside .contents()

If I do this:

function getAllTextNodes(root) {
  root = $(root || "body");
  return root.find("*:not(iframe)").contents().filter(function() {
    return this.nodeType === 3 && //Node.TEXT_NODE = 3
      $.trim(this.nodeValue) !== "";
  });
}

getAllTextNodes($.parseHTML("<div><div>a<div>sub</div>b</div></div>"))

the result is an array with "a", "b" and "sub". So it seems that they traverse the structure and when they reach an element they work on that element entirely before they continue with the nested elements.

While this may make sense (or it doesn't matter in some cases) it causes some troubles on my end, because I need a logic that returns the elements in the exact same order they appear in the DOM-tree, i.e. I'd be happy to see "a", "sub" and "b" being returned.

Is that something that jQuery built on purpose? Can I change the order somehow? Or is this a bug?

Upvotes: 1

Views: 32

Answers (1)

Bergi
Bergi

Reputation: 665090

Is that something that jQuery built on purpose? Or is this a bug?

I don't think it's done on purpose, but given that selector APIs and even most modification methods do have their results in DOM order, it might be considered a bug. From what you show, it looks like contents is implemented with a simple flatMap(el => el.childNodes).

Can I change the order somehow?

Yes, you can use jQuery.uniqueSort() on the jQuery object, which uses Node.compareDocumentPosition internally:

return $.uniqueSort(root.find("*:not(iframe)").contents().filter(function() {
    return this.nodeType === 3 && $.trim(this.nodeValue) !== "";
}));

However, jQuery isn't great with text nodes anyway. It might be simpler to use a native DOM API here, such as NodeIterator:

const it = document.createNodeIterator(root[0], NodeFilter.SHOW_TEXT, node => node.data.trim() != ""),
      res = [];
for (let node; node = it.nextNode(); )
    res.push(node);
return res;

Upvotes: 1

Related Questions