2

I am having problems with an advanced negation pseudo-class selector in a :not(s) selector (!!in Google Chrome!!).

I have this HTML markup:

<div class="body">
    <div class="container">
        <div class="element-1"></div>
        <div class="element-2"></div>
        <div class="element-1 element-offset-2"></div>
        <div class="element-3"></div>
    </div>
</div>

And I use the following CSS to have a margin-left on every element- element (except the first one):

div[class*="element-"] + div[class*="element-"] {
    margin-left: 1%;
}

See https://jsfiddle.net/em6hefqj/2/ for a working example.


But here is the problem, I don't want the element with the class element-offset-n to have the margin-left. I use the following CSS for that:
div[class*="element-"] + div[class*="element-"]:not([class*="element-offset-"]) {
    margin-left: 1%;
}

See https://jsfiddle.net/em6hefqj/ for a working example.


As you can see above, I use [class*="element-offset-"], but I have to use div[class*="element-offset-"]. I cannot use the advanced selector without the element prefix because it will cause conflicts between different elements on the same page (div, p and a few more). This works (what I have tested) in Safari and Firefox, but it does not work in Google Chrome See https://jsfiddle.net/em6hefqj/1/ for a (in Chrome) not working example.

Here are some images for reference:

Chrome (works without div in front of the selector): Chrome working; without div in front of the advanced selector

Safari (works with div in front of the selector): Safari working; with div in front of the advanced selector

Chrome (does not work with div in front of the selector): Chrome does not work; with div in front of the advanced selector


I hope you can help me with this, I am open to suggestions. This might be a bug if so I'll report it to the Chrome developer team.
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Tom Udding
  • 2,264
  • 3
  • 20
  • 30
  • This is fascinating - as far as I can tell this does indeed look like a bug with Chrome. i take this back given Mr Lister's answer below – ESR May 01 '17 at 06:48
  • 1
    Sorry, but how does `div[class*="element-"]:not([class*="element-offset-"])` cause a problem with other elements? This compound selector can only select divs, regardless of the `:not`. – Mr Lister May 01 '17 at 06:50
  • @MrLister the `div` after the `+` should not be there, might have made a typo while copying the code (I have changed the element names, that might be the cause). It should be `(...) + [class*="element-"]:not(div[class*="element-offset-"])`. – Tom Udding May 01 '17 at 06:51
  • 1
    Good point @MrLister. As a side note, OP, you could try `div[class*="element-"]:not(:first-child):not([class*="element-offset-"])` instead of `div[class*="element-"] + div[class*="element-"]:not([class*="element-offset-"])` – ESR May 01 '17 at 06:52

3 Answers3

1

I believe Chrome is correct here (At least as per current standard)

documentation:

6.6.7. The negation pseudo-class

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.

w3c doc

vals
  • 61,425
  • 11
  • 89
  • 138
  • That would depend on your definition of "simple selector" then. I read that as "not a compound selector", so `:not(div + div)` would be out. But `:not(div[class])` would still work. – Mr Lister May 01 '17 at 06:54
  • @Mr Lister: div + div is a complex selector. div[class] is a compound selector ("sequence of simple selectors" in L3). Neither are simple selectors. Both Selectors L3 and L4 are clear in their definitions of "simple selector" - it's the definition of :not() that has changed. – BoltClock May 01 '17 at 07:22
  • @Mr Lister: The definition of "simple selector" that means "not a complex selector" is from CSS2 - I'm surprised they didn't change it, considering the vast array of other in-place changes they made to that spec. – BoltClock May 01 '17 at 07:29
  • @BoltClock OK. Ehm, wait a minute, isn't `[class]` actually a shorthand notation for `*[class]`? Why would that be working then, while `div[class]` does not? – Mr Lister May 01 '17 at 07:30
  • It's amazing how *hard* it's making a spec that when readed, everybody understands the same ... – vals May 01 '17 at 07:32
  • @Mr Lister: The `*` is semantically implied, not grammatically. `*[class]` still counts as two simple selectors, and `[class]` still counts as one, even though they are functionally equivalent. If you wrote `:not(*[class])`, it would still fail in browsers that don't implement L4. – BoltClock May 01 '17 at 07:32
  • I guess that usually, * is just dropped by the parsing engine, (or a later optimization) – vals May 01 '17 at 07:35
1

:not() only takes a "simple" selector according to the spec.

You can use the classic CSS approach of giving two rules, with the second overriding the first if applicable:

div[class*="element-"] + div[class*="element-"] {
  margin-left: 1%;
}

div[class*="element-"] + div[class*="element-"][class*="element-offset-"]) {
  margin-left: 0;
}
  • How could I not think about this myself... Thanks! It seems that Safari has introduced some level 4 selectors and this is part of it (if you go deep enough in the specifications). – Tom Udding May 01 '17 at 07:00
0

I think this is a cross browser compatibility issue. This could help https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS

Ashan
  • 31
  • 1
  • 6