Why Does the General-Sibling Combinator Allow Toggling Pseudo-Element's Content, But Not the Adjacent-Sibling

Why does the general-sibling combinator allow toggling pseudo-element's content, but not the adjacent-sibling?

This is a long-standing bug in WebKit browsers related to the use of certain dynamic pseudo-classes with next-sibling combinators. This happens whether you're applying styles to the sibling element itself or a pseudo-element of that sibling element.

I don't know if anybody has filed a bug report yet, but this has been seen rather frequently on the site:

  • Webkit bug with `:hover` and multiple adjacent-sibling selectors
  • CSS adjacent sibling selectors, Safari and <nav> elements

Strangely it was also reported that Chrome had issues with the general sibling combinator, but as you note it works in your given scenario:

  • Why doesn't this CSS selector work: a:hover ~ span?

So either that was fixed, or something else triggers/triggered it.

CSS general sibling combinator (~) not working with negation pseudo-class (:not)

When all else fails, RTFM. I found the answer to my question in the W3 selector specification:

The negation pseudo-class, :not(X), is a functional notation taking a
simple selector (excluding the negation pseudo-class itself) as an
argument.

and

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

In other words, it doesn't work because it shouldn't work. The general sibling combinator is not a simple selector, so it cannot be used with the negation pseudo-class.

Links:

  1. http://www.w3.org/TR/css3-selectors/#negation
  2. http://www.w3.org/TR/css3-selectors/#simple-selectors-dfn

Next-sibling combinator should not work with complex selectors

In the specification you can read:

The next-sibling combinator is made of the "plus sign" (U+002B, +) character that separates two sequences of simple selectors. The elements represented by the two sequences share the same parent in the document tree and the element represented by the first sequence immediately precedes the element represented by the second one.

Nothing is saying that it should only work for "sequences of simple selectors". It's simply saying that the + character separates two sequences of simple selectors and it restricts nothing beyond this so you can have more selectors.

Let's consider the following examples:

.a .b + .b
.b + .b
span.a:not(.b) .b + .b > div

In all of them we have the portion .b + .b which is described by:

the "plus sign" (U+002B, +) character that separates two sequences of simple selectors.


If you read at the start of the specification you will find the generic rule that confirms this:

A selector is a chain of one or more sequences of simple selectors separated by combinators. One pseudo-element may be appended to the last sequence of simple selectors in a selector.

A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator. It always begins with a type selector or a universal selector. No other type selector or universal selector is allowed in the sequence.

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

Basically we can have

Seq_simple_selectors + Seq_simple_selectors Seq_simple_selectors > Seq_simple_selectors

Then inside each Seq_simple_selectors we can have something like:

 div.a.b#id::before

If we consider your example .a .b + .b you have 3 sequences and in each sequence only one simple selector.

You second example is also fine and working if you have the correct HTML that goes with:

.b + .a .b {
background: red;
height: 20px;
}
<div class="b"></div>
<div class="a">
<div class="b"></div>
</div>

:empty pseudo class issue with added/removed content and sibling combinators

Something like this will work, but it's not particularly pretty:

ul:empty {}

ul:empty + div {
color: red;
}

ul + div {
animation: repaint 1000s infinite linear;
}

@keyframes repaint {
from { zoom: 1; }
to { zoom: 0.99999; }
}

See: http://jsfiddle.net/vd2Hx/

Tested in Chrome, Firefox, Safari, Opera, and IE.

Webkit bug with `:hover` and multiple adjacent-sibling selectors

you can overcome Webkit's pseudo classes + general/adjacent sibling selectors bugs by faking animation on the body element:

body { -webkit-animation: bugfix infinite 1s; }

@-webkit-keyframes bugfix {
from { padding: 0; }
to { padding: 0; }
}

you can check it out here: http://jsfiddle.net/jalbertbowdenii/ds2yY/1/

Issue with adjacent CSS Selector in Chrome and in Safari

You can try using the sibling combinator. ~ is similar to +, however, it’s less strict. While an adjacent selector will only select the first element that is immediately preceded by the former selector, this one is more generalized. It will select any elements as long as they follow the former selector in the tree.

So, your CSS would look like this...

input[type="checkbox"]:checked + span {
display:none
}

input[type="checkbox"] + span {
display:block
}

input[type="checkbox"]:checked ~ span ~ span {
display:block
}

input[type="checkbox"] + span + span {
display:none
}

Here is a working example, provided by @Adrift, using the above code: jsfiddle.net/YDuzC/10

Why doesn't the adjacent sibling selector work?

Your html is invalid. p element permitted content is phrasing content. Additional css adjacent sibling selector selects following the rule:

Adjacent sibling selectors have the following syntax: E1 + E2, where
E2 is the subject of the selector. The selector matches if E1 and E2
share the same parent in the document tree and E1 immediately precedes
E2, ignoring non-element nodes (such as text nodes and comments).

in your example element with id #p2 is not immediately precedes h4. You can fix your html and use general sibling selector:

#p2 ~ h4 {

color: red;

}
<div>

<p id="p2">This is the sibling of the selected para</p>

<div>

<h4>this should not be colored</h4>

</div>

<h4>this should be colored</h4>

</div>

CSS General Sibling Selector Specificity

Both selectors have the same specificity, what causes h2 ~ p to take precedence is that it is defined after, therefore cascading over h1 ~ p. How close the sibling are is of no consequence.

For the behavior you want you can use the adjacent sibling selector +.

If you change the h1 ~ p after you will see it takes precidience

h2 ~ p {

color: green;

}

h1 ~ p {

color: red;

}
<h1>Section 1</h1>

<p>I came after a h1 and should be red.</p>



<h2>Section 2</h2>

<p>I came after a h2 and should be green.</p>

<h1>Section 3</h1>

<p>I should be the same color as the section one text?</p>


Related Topics



Leave a reply



Submit