¿Por qué el combinador de hermanos generales permite alternar el contenido de los pseudo-elementos, pero no el hermano adyacente?

En esta pregunta ” Selector de CSS3 que funciona como .click () de jQuery”, publiqué una respuesta usando el estado :checked de una input , de type="checkbox" para alternar la visualización de un elemento.

Este es el HTML de la demostración que publiqué en esa respuesta:

    

Y el CSS (con transiciones eliminadas por brevedad):

 #switch { display: none; } #switch + nav { height: 0; overflow: hidden; /* transitions followed */ } #switch:checked + nav { height: 4em; color: #000; background-color: #ffa; /* transitions followed */ } label { cursor: pointer; } 

Demostración de JS Fiddle .

Una vez que había publicado la respuesta, se me ocurrió que también podíamos alternar el texto de la label utilizada para activar el cambio de estado de esa casilla, utilizando los siguientes selectores (habiendo modificado el texto de la label para ‘navegación’):

 label { display: inline-block; cursor: pointer; } #switch + nav + label::before { content: 'Show '; } #switch:checked + nav + label::before { content: 'Hide '; } 

Simplified / basic JS Fiddle demo .

Esto no funcionó, ya que mientras el selector coincidía mientras la input estaba en su estado desactivado (y la label mostraba Show navigation ), el selector no coincidía cuando el estado de la input cambiaba. Tenga en cuenta que las transiciones todavía se efectuaron en el elemento de nav , y el selector de coincidencia original indica que el combinador del siguiente hermano coincidió originalmente. El enlace de arriba muestra una demostración simplificada de los selectores que no funcionan (en Chrome 27 / Windows XP).

Entonces se me ocurrió probar con el combinador de hermanos y hermanas, para reducir la cadena del selector. que resultó en el siguiente CSS (con transiciones nuevamente despojadas por brevedad):

 #switch:checked + nav { background-color: #ffa; } label { display: inline-block; cursor: pointer; } #switch ~ label::before { content: 'Show '; } #switch:checked ~ label::before { content: 'Hide '; } 

Demostración de JS Fiddle .

Para mi sorpresa, esto funcionó (el content de la label cambió en respuesta al cambio de estado de la input ).

Entonces, la pregunta: ¿por qué el combinador de hermanos generales permite la actualización de un hermano posterior, mientras que los combinadores de próximo hermano encadenados (que coinciden con los elementos y la estructura del DOM) no lo hacen?

Además, esto parece funcionar en Firefox (21, en Windows XP); así que supongo que la pregunta se modifica ligeramente para incluir: ¿esto es un error en Chrome / Webkit, o un comportamiento esperado?

Y, aún más , parece que si bien esto es un error en Chrome (gracias @Boltclock), hay una animación un tanto absurda de “no hacer nada” que corrige la demo no funcional (aunque existen otras alternativas, tal vez mejores, como las de Scott la respuesta muestra):

 body { -webkit-animation: bugfix infinite 1s; } @-webkit-keyframes bugfix { from { padding: 0; } to { padding: 0; } } #switch { } #switch + nav { -moz-transition: all 1s linear; -ms-transition: all 1s linear; -o-transition: all 1s linear; -webkit-transition: all 1s linear; transition: all 1s linear; } #switch:checked + nav { background-color: #ffa; -moz-transition: all 1s linear; -ms-transition: all 1s linear; -o-transition: all 1s linear; -webkit-transition: all 1s linear; transition: all 1s linear; } label { display: inline-block; cursor: pointer; } #switch + nav + label::before { content:'Show '; } #switch:checked + nav + label::before { content:'Hide '; } 

Demostración de JS Fiddle .

Nota: la razón por la que estoy actualizando la pregunta con este “arreglo”, en lugar de publicarla como respuesta, es simplemente porque la pregunta no era “¿cómo puedo solucionar esto?” pero (básicamente) “¿por qué no funciona?”

Este es un error de larga data en los navegadores WebKit relacionado con el uso de ciertas pseudo-clases dinámicas con los combinadores de los próximos hermanos. Esto sucede tanto si aplicas estilos al elemento hermano en sí o un pseudo-elemento de ese elemento hermano.

No sé si alguien ha presentado un informe de error aún, pero esto se ha visto con bastante frecuencia en el sitio:

  • Fallo de Webkit con `: hover` y múltiples selectores de hermanos adyacentes
  • Selectores de hermanos adyacentes de CSS, elementos de Safari y

Extrañamente, también se informó que Chrome tuvo problemas con el combinador de hermanos general, pero como nota que funciona en su escenario dado:

  • ¿Por qué este selector de CSS no funciona: a: hover ~ span?

Entonces, o eso fue reparado, o algo más lo desencadenó / activó.

Bug trabaja alrededor

Aparentemente, ciertas pseudo-clases válidas encadenadas juntas le permiten funcionar.

Estos trabajos (vea Fiddle # 1 , Fiddle # 2 , Fiddle # 3 ):

 #switch:checked + nav:only-of-type + label::before #switch:checked + nav:nth-of-type(1) + label::before #switch:checked + nav:nth-child(2) + label::before 

Esto no fue así (ver Fiddle # 4 ):

 #switch:checked + nav:not([class]) + label::before 

Probé algunas otras combinaciones :not() , ninguna de las cuales le permitió funcionar.

** Mejor opción **

 #switch:checked + nav:nth-child(n) + label::before