0

I'm using this snippet and it works in Firefox and Chrome, but not in Safari. Why?

.column:not(.custom.no-edit)

If I change it to this it seems to work, but that doesn't give the same effect does it? If I'm thinking correctly the first one will give true if both classes are present while the second will give true if any of the classes are present?

.column:not(.custom):not(.no-edit)

TylerH
  • 20,799
  • 66
  • 75
  • 101
SeaBass
  • 1,584
  • 4
  • 20
  • 46
  • 1
    What version of Safari? Also, please include the HTML you're trying to select/avoid. We need to see a [mcve]. – TylerH Apr 02 '20 at 19:03
  • Pretty sure this is a duplicate of https://stackoverflow.com/questions/5684160/can-the-not-pseudo-class-have-multiple-arguments but need to see a [mcve] to be sure. – TylerH Apr 02 '20 at 19:06
  • 1
    Selectors are cumulative (they are ANDed together); that's why `.custom.no-edit` matches only those elements with both `.custom` and `.no-edit`. So, `:not(.custom):not(.no-edit)` is likewise cumulative, matching only those elements that have neither `.custom` nor `.no-edit`. – Heretic Monkey Apr 02 '20 at 19:12
  • Ok, got it. Thanks! – SeaBass Apr 02 '20 at 19:14

2 Answers2

2

Consider the following HTML fragment:

<div class="column"></div>
<div class="column custom"></div>
<div class="column no-edit"></div>
<div class="column custom no-edit"></div>

The selector

.column:not(.custom.no-edit)

is level 4 syntax that matches .column elements that don't have both "custom" and "no-edit" in their class attributes. This means that .column elements that have either one or the other will match. All of the first three elements in the fragment will match — only the fourth will not.

In level 3 implementations, the syntax is considered invalid and gets ignored, resulting in none of the elements in the fragment matching the selector.

The level 3 equivalent that's understood by Firefox and Chrome is

.column:not(.custom), .column:not(.no-edit)

The selector

.column:not(.custom):not(.no-edit)

is level 3 syntax that matches .column elements that don't have either "custom" or "no-edit" in their class attributes. Only the first element in the fragment will match this selector. The level 4 equivalent is

.column:not(.custom, .no-edit)

Hopefully this helps you understand the results you expect and the results you actually get.

See also:

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
1

Are you sure .column:not(.custom.no-edit) worked in chrome and FF but not safari ?

That is a CSS level 4 syntax. However Safari is known to support it.

Let's take a look at the spec.


CSS level 3

The The negation pseudo-class, :not(X), is a functional notation taking a simple selector (excluding the negation pseudo-class itself) as an argument. It represents an element that is not represented by its argument.

A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

Something that is not mentioned in the spec, :not() does not accept compound Selectors

A compound selector is a sequence of simple selectors that are not separated by a combinator, and represents a set of simultaneous conditions on a single element.

Basically chaining classes and types etc.., Example:

.class1.class2.class3
#myId.MyClass
div.MyClass#yourId

CSS level 4

The The negation pseudo-class, :not(), is a functional pseudo-class taking a selector list as an argument. It represents an element that is not represented by its argument.

In CSS level 4 it will become free for all.


Is div:not(.a):not(.b) equal to div:not(.a.b)

div {
  display: inline-block;
  width: 80px;
  height: 80px;
  border: 4px solid black;
}

div:not(.a.b) {
  background-color: green;
}
<h3>div { ... }: select all divs</h3>
<h3>.a.b { ... }: select all elements with both classes .a and.b </h3>

<h3>If we combine the two using :not() The first two boxes should be green</h3>


<div class="a"></div>
<div class="b"></div>
<div class="a b"></div>

If the above selector were to work it will select everything except those elements that have both classes a and b

Which means it is not equivalent to div:not(.a):not(.b) as this will ignore all elements that have both classes or either one, And we only want to ignore elements that have both.


Solution

We can make use of attribute selectors until css level 4 drops in, if you don't mind a bit of care.

div:not([class^="a b"])

This will select all div elements except those that begin with both classes a and b other classes can be applied to the element so styling will be normal.

Your classes must always be preceded by the string a b in order for this to work, same goes the opposite selector [attr$=value]

div {
  display: inline-block;
  width: 80px;
  height: 80px;
  border: 4px solid black;
}

div:not([class^="a b"]) {
  background-color: green;
}
<div class="a"></div>
<div class="b"></div>
<div class="a b some other classes"></div>

Most browsers support attribute selectors, so support won't be a problem.

Rainbow
  • 6,772
  • 3
  • 11
  • 28
  • That is brilliant. Thank you! How do you use `[attr$=value]`? Edit. Just read that it looks from the back of the string. Got it! – SeaBass Apr 09 '20 at 03:31
  • This is valid level 4 syntax. If @SeaBass is reporting that it works in Firefox and Chrome but not Safari, chances are they have their expectations backwards. This syntax is known to work in Safari but not the others. – BoltClock Apr 13 '20 at 04:38
  • @BoltClock That's exactly what happened when i tested it, it didn't work in chrome nor FF being that is not supported yet, However when i looked it up it turns out that it actually works in safari only, But the op saying that it didn't work in safari had me confused because i don't have safari in reach. I'll add it to the answer :) – Rainbow Apr 13 '20 at 10:18