I want to writ a DRY CSS Selector.
The selector should match elements within a group, but not elements within a group that is a child of the given group.
Elements can only appear as direct children of a given group, or they are wrapped in an extra element.
For the given DOM the standard selector would be: .grp.A > .el, .grp.A > * > .el
This can be made DRYer by using: :is(.grp.A, .grp.A > *) * .el
For me the DRYest solution would be something like this: .grp.A:is(*, * > *) > .el. But it doesn't work. Do you have any suggestions on how to improve this selector further?
console.log([...document.querySelectorAll(".grp.A > .el, .grp.A > * > .el")]);
console.log([...document.querySelectorAll(":is(.grp.A, .grp.A > *) > .el")]);
console.log([...document.querySelectorAll(".grp.A:is(*, * > *) > .el")]);
<div class="grp A">
<div class="cont">
<div class="grp B">
<div class="el noooooo"></div>
</div>
</div>
<div class="el yes1"></div>
<div>
<div class="el yes2"></div>
</div>
</div>
When it comes to CSS selectors, I would prioritize readability over being concise. The first selector you show is the most readable
.grp.A > .el, .grp.A > * > .el
As for your third attempt, to quote the CSS Level 4 spec:
Default namespace declarations do not affect the compound selector representing the subject of any selector within a :is() pseudo-class, unless that compound selector contains an explicit universal selector or type selector.
Had to read this many times, but my understanding is that :is() is global scope and the selectors in it can't be compounded based on what's beforehand.
I've added an additional example where a compounding selector within :is doesn't work based off what's beforehand.
// Direct child .el of .grp.A or direct child .el of any direct child of .grp.A
console.log([...document.querySelectorAll(".grp.A > .el, .grp.A > * > .el")]);
// Direct child .el of .grp.A or direct child .el of any direct child of .grp.A
console.log([...document.querySelectorAll(":is(.grp.A, .grp.A > *) > .el")]);
// Direct child .el of .grp.A that is any element, or
// that is any element that's a direct child of any element
console.log([...document.querySelectorAll(".grp.A:is(*, * > *) > .el")]);
//.grp.A satisfies criteria of being anything, all equating to .grp.A > .el
// Additional example that compounds don't work for :is()
// Only .A of the two arguments in the :is list works
console.log([...document.querySelectorAll(".grp:is(.A, .A > *) > .el")]);
<div class="grp A">
<div class="cont">
<div class="grp B">
<div class="el noooooo"></div>
</div>
</div>
<div class="el yes1"></div>
<div>
<div class="el yes2"></div>
</div>
</div>