How can I override a selector with many negations (:not)?
Asked Answered
S

2

6

For example to style standard inputs I write something like:

input:not([type="checkbox"]):not([type="radio"]) {
    background-color: blue;
}

However that increases the specificity a lot so if I want to override it using a class I have to do something like:

.red.red.red {
    background-color: red;
}

Is there a way to decrease the specificity of the original input selector without changing the functionality?

Statics answered 8/3, 2017 at 20:29 Comment(3)
You can always use !important to override specificity.Barnyard
specificity.keegan.st use this to find the right selectorOstensorium
@MichaelCoker yes, however that destroys the specificity. I try to keep the specificity as low as possible, and for me using .red.red.red is a better solution than !important.Statics
C
4

Unfortunately, in Selectors 3, that's the best you can do since :not() only accepts a single simple selector. Having said that, the selector in your overriding rule can be replaced with input.red.red — you don't need more class selectors than attribute selectors unless that rule has to appear earlier in the stylesheet.

This is another problem that Selectors 4 solves with its enhancement of :not(): specificity. In Selectors 4, you will be able to replace all those negations with just one :not() pseudo, effectively decreasing its specificity within your original selector to just one attribute selector. From section 16:

The specificity of a :not() pseudo-class is replaced by the specificity of the most specific complex selector in its selector list argument.

Since any attribute selector is equally specific to a class selector, the specificity of any number of lone attribute selectors in a list will never be more than that of just one of them. This allows you to get away with using just one class selector for your override without having to repeat it.

The following works in Safari as a proof-of-concept:

/* 1 type, 2 attributes -> specificity = (0, 2, 1)
input:not([type="checkbox"]):not([type="radio"]),
   1 type, 1 attribute  -> specificity = (0, 1, 1) */
input:not([type="checkbox"], [type="radio"]) {
    background-color: blue;
}

/* 1 type, 1 class      -> specificity = (0, 1, 1) */
input.red {
    background-color: red;
}
<input type="checkbox">
<input type="radio">
<input type="text">
<input class="red" type="text">
Clercq answered 9/3, 2017 at 2:56 Comment(0)
Z
1

This may get you around the specificity issue:

input:not([type="checkbox"]):not([type="radio"]):not(.red)) {
    background-color: blue;
}

.red {
  background-color: red;
}
<input type="checkbox"><br>
<input type="radio"><br>
<input class="red">
Zeke answered 8/3, 2017 at 20:37 Comment(9)
It's creative I'll give you that :D. Unfortunately it's not very practical as you want .red to still inherit the original styles but with the ability to override the original styles. My example didn't show this, sorry.Statics
Yeah, I didn't know all the details, so I threw in a concept. I knew it was a long-shot.Zeke
Thanks, I appreciate the effort!Statics
This is invalid - you cannot nest multiple :not()s within one another, not even in L4.Clercq
@BoltClock, that was a typo. Needed a closing bracket. Not intending to nest :not()s.Zeke
Wait... when did they remove the restriction? I had no idea. So :not([type="radio"]:not(.red)) is valid. How about that...Clercq
Ha, I never new it was restricted. I've always known :not() to be able to chain together. But your comment led me to identify a missing bracket, which indeed created a nested :not(), which is invalid. @ClercqZeke
L4 :not() is designed to solve the specificity problem though. Check out my answer. (To be honest I bugged joshhunt into posting this question as we were discussing it on chat just yesterday...)Clercq
Look at that entire world I didn't know about. Now I can see how incredibly out-of-context my answer is ;-) @ClercqZeke

© 2022 - 2024 — McMap. All rights reserved.