All questions

getElementsByTagNameHierarchy

Premium

getElementsByTagNameHierarchy

Given a root element and a space-separated chain of tag names like 'div span a', find every element matching the last tag in the chain that also has — somewhere above it — elements matching each earlier tag, in the same nesting order. This is the CSS descendant combinator (div p means "every p inside a div") reduced to tag names: the earlier tags must be ancestors of the candidate, but they need not be direct parents.

Signature

function getElementsByTagNameHierarchy(root: Element, selector: string): Element[];

selector is one or more tag names separated by whitespace, ordered ancestor to descendant. Matching is case-insensitive. Returns a plain array in document order; the root itself is never a candidate or an ancestor.

Examples

const root = document.createElement('div');
root.innerHTML = '<div><section><p>a</p></section></div>';

getElementsByTagNameHierarchy(root, 'div p');
// → [<p>a</p>]   (the p has a div ancestor, even though section sits between)
root.innerHTML = '<div><p>a</p></div><section><p>b</p></section>';
getElementsByTagNameHierarchy(root, 'div p');
// → [<p>a</p>]   (b has no div ancestor, so it is excluded)

root.innerHTML = '<ul><li><a>x</a></li></ul>';
getElementsByTagNameHierarchy(root, 'ul li a'); // → [<a>x</a>]

Notes

  • Descendant, not direct child. Each earlier tag must appear somewhere up the candidate's ancestor chain — like the CSS descendant combinator, not the child combinator. 'div p' matches <div><section><p></p></section></div>.
  • Order is fixed. The chain reads ancestor to descendant. 'p div' is a different query from 'div p' and generally returns different elements.
  • Ancestors must nest in order. For 'a b c', a candidate c needs a b ancestor that itself has an a ancestor — the tags must appear in that outward order, not merely all be present.
  • Root excluded. The root is never returned and never counts as one of the ancestors in the chain.
  • Case-insensitive. 'DIV P' and 'div p' behave identically; element tagName is upper-cased internally.
  • Document order, no duplicates. Results come back in pre-order traversal order, each matching element appearing exactly once. A single-tag selector behaves like a plain descendant tag search.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium