(This post has been edited since its original proposal to remove ancestorQuerySelector, which was essentially the same as Element.parentElement.closest, and rename ancestorQuerySelectorAll to simply ancestors.)
Pretty self-explanatory: where Element.querySelector returns the first matching child element, Element.querySelectorAll returns all matching child elements, and Element.closest returns the first ancestor matching a given selector query, Element.ancestors would return a NodeList (like querySelectorAll) of all ancestors matching a given selector query (similar to jQuery’s parents() function).
Implementation / prollyfill:
Element.ancestors = function ancestors(selector) {
// this would use a (non-live) NodeList, but there doesn't appear to be an API for that
var matchingAncestors = [];
var parent = this.parentElement;
while (parent) {
if (parent.matches(selector)) matchingAncestors.push(parent);
parent = parent.parentElement;
}
return parent;
}
@stuartpb as @MT said Element#closest() returns the single Element parent, you would need to think of a method that returns a NodeList or the future Elements collection, which its name is close to closest(). I don’t think we’ll want to have closest and ancestorQuerySelectorAll.
Only thing I can think of is closestAll but don’t really like it.
Ah yes, I’d misinterpreted the name - it sounds like something that’s going to select siblings and/or children (and it didn’t turn up when I Googled “ancestor query selector”). Reading the docs now, I see that it is only ancestors, so yes, this is what I was proposing with ancestorQuerySelector.
As @Edwin_Reynoso says, closest doesn’t tackle the ancestorQuerySelectorAll case, which could maybe be added as listClosest.
parents() could work since it has the letter s. The only other thing with the string “parent” is parentElement and parentNode since parents is a method I think it’ll make sense?
But still doesn’t makes sense kind of weird, so closestParents()??? or closestAncestors() since closest is not really a parent of the element its being called on.
TBH I’m not sure when is this ever wanted. Like when do you ever want to query multiple parents, which is probably why only closest() was created and created to return 1 Element
Well, there’s also furthest(), which would want to address the highest parent matching a selector - and sometimes you do want to affect multiple levels of parent (ie. changing background colors in a zebra-striping fashion).
var levels = target.ancestors('.differentiated-level');
var d = 360 / (1+Math.PHI);
for (var i = 0; i < ancestors.length; i++) {
levels[i].style.backgroundColor = 'hsl(' + (i*d % 360) + ',100%, 50%)';
}
This is more like a Fruit Stripe zebra, but it’s a demo of the general behavior I was talking about.
Now, of course, the issue with that example is that you could just as easily do repeated calls to .closest with an increment of i in a while (closest) loop - but for other cases (eg. counting how many levels a node is deep in a selector), .ancestors() is significantly easier (although the savings at this point are more bikeshedding, and I honestly don’t feel the need to argue in favor of ancestors() so hard when closest() already exists).
Also, I just noticed: .closest can select the element itself - I suppose the element.ancestorQuerySelector I was proposing would be equivalent to element.parentElement.closest.