I noticed recently that :focus-within
can be entirely replaced with :has(:focus)
. What I find interesting is that they were both added in Level 4 of specification, so is there a difference between the two that I missed?
Reading MDN, I found one difference where :focus-within
should work on the shadow dom elements, a direct quote: “This includes descendants in shadow trees”, but I can’t seem to get that to work.
The actual specification, backs my testing, as it says “The … pseudo-class applies to …, as well as element (or pseudo-element) whose descendant in the flat tree (including non-element nodes, such as text nodes) matches the conditions for matching :focus.”, which directly contradicts MDN (as there is no mention of the shadow tree).
One actual difference I’ve found is :focus-within, also includes the element you’re focusing on. But “within” implies “children elements”, not “current element & its children”. If you need it to match “current element & its children”, you could use &:focus, &:has(:focus)
(could also use :is()
if you’re not CSS nesting). Which would make more logical/linguistic sentence than :focus-within
.
Example:
An example of using :focus-within
, and sample code of how :has(:focus)
can entirely replace it would be the awesome accessibility feature “Skip to Main content”.
HTML: <div class="skip-link">Skip to <a href='#main'>content</a> or <a href='#footer'>footer</a></div>
CSS (:focus-within):
.skip-link {
transform: translateY(-100%);
&:focus-within {
transform: translateY(0%);
}
}
CSS (:has(:focus)):
.skip-link {
transform: translateY(-100%);
&:has(:focus) {
transform: translateY(0%);
}
}
Are there any other differences between the two?