Specificity in :not(), :has(), and :matches()
Published 6 years, 5 months pastA few years back, I wrote a short post on specificity, element proximity, and the negation pseudo-class. Everything in it is still accurate and relevant, but I have some updates to share.
First off, I’d like to clarify something that some people may have found confusing. In that post, I said:
But it turns out that the negation pseudo-class isn’t counted as a pseudo-class.
That might leave some people with the idea that the entire negation portion of the selector is ignored for the purposes of specificity, especially if you don’t speak spec.
So consider the following:
div:not(.one) p
In order from left to right, that’s an element selector (div
), a negation pesudo-class (:not
) a class selector (.one
), and another element selection (p
). Two element selectors and one class selector are counted towards the specificity, yielding a total of 0,0,1,2
. That’s the same specificity as div.one p
, though the two selectors select very different things.
In Ye Olden Days, that was easy enough to work out, because :not()
could only ever contain a simple selector. Things are looking to get more complicated, however — :not()
is set to accept grouped selectors. So we will at some point be able to say:
div:not(.one, .two, #navbar) p
So any p
element that is not descended from a div
that has a class
containing either one
or two
(or both), or that has an id
of navbar
, will be selected.
But how do we calculate the specificity of that whole selector? Just add up all the pieces? No. The Working Group recently decided that the specificity contributed from inside a :not()
will be equal to the single selector with the highest specificity. So given div:not(.one, .two, #navbar) p
, the #navbar
will contribute 0,1,0,0
to the overall specificity of the selector, yielding a total of 0,1,0,2
. The specificities of .one
and .two
are ignored.
This same approach will be taken with the :has()
and :matches()
pseudo-classes. Thus we get the following:
:matches(nav, header, footer#pageend) a[href] {color: silver;} /* 0,1,1,2 */
article:has(a.external, a img) /* 0,0,1,2 */
input:not([type="radio"], [type="checkbox"]) /* 0,0,1,1 */
In the first instance, the bits that are added together are footer#pageend
and a[href]
, so that’s one ID, one attribute, and two elements. In the second, it’s article
and a.external
for one class and two elements. And last, we add up input
and either of the [type=""]
attribute selectors, since their specificities are equal, which means we add up one attribute and one element.
There is still, so far as I’m aware, no concept of DOM-tree proximity in CSS. I would still continue to wager that will remain true, though I’d put a fair bit less money down now than I would have six years ago.
Comments (6)
That is, as far as I can tell, what Safari has implemented when they added support for Selectors level 4 (since Safari v9 !) – at the very least for
:not()
and:matches()
.Other fun is the specificity with the child-indexed pseudo-classes (
:nth-child(-n+3 of li.important) { /* 0,2,1 */ }
)Pingback ::
Weekly Roundup of Web Design and Development News: June 8, 2018
[…] Specificity in :not(), :has(), and :matches(): Eric Meyer walks you through how specificity will be calculated for grouped selectors in :not(), :has(), and :matches (). […]
Pingback ::
Collective #422 | Web Design News from Dubado
[…] Specificity in :not(), :has(), and :matches() […]
I’ve read others muse about how
:not()
’s new rules can be used to circumvent specificity entirely:…would ostensibly be the same as
whatever#id, whatever.multiple.classes
, but with only the specificity of a single element.Pingback ::
Pixels of the Week – June 17, 2018 - Stéphanie Walter - UX and UI Design, Mobile optimization EN
[…] Specificity in :not(), :has(), and :matches() […]
Pingback ::
Специфичность CSS для :not(), :has() и :matches() | Все про сайтостроение
[…] Источник: https://meyerweb.com/ […]