¿Cómo selecciono un elemento basado en el estado de otro elemento en la página con CSS?

Tengo elementos que pueden reflejar diferentes estados, ya sea desencadenados por el usuario ( :hover :focus , etc.) o manipulados por el servidor ( data-status="finished" , disabled , etc.).

Puedo apuntar al elemento que tiene un estado cambiado, pero parece que no puedo encontrar una manera de apuntar a otros elementos en el DOM en función del estado del elemento en cuestión.

Ejemplo:

 
Element 1
Element 2
Element 3
Element 4
Element 4
Element 4
Element 4
...

o simplemente renderice los elementos con los estilos apropiados del lado del servidor.

¿Hay un selector CSS que me permita especificar qué elementos deberían seleccionarse en función del estado del elemento objective?

Algo como:

 div[data-status~=finished]:affect(.blink, .spin) 

eso me permitiría también apuntar elementos que no tienen el mismo padre con CSS solamente?

La respuesta general a la pregunta canónica

¿Cómo selecciono un elemento basado en el estado de otro elemento en la página con CSS?

es que depende de exactamente tres condiciones:

  1. si el estado de estos elementos se puede representar utilizando selectores simples,
  2. si una relación estructural se puede express entre estos dos elementos utilizando combinadores para formar un solo selector complejo , y
  3. si el elemento que desea orientar puede convertirse en el sujeto del selector complejo resultante.

Si bien el estándar actual de Selectors tiene algunas características interesantes y, a veces, potencialmente potentes, la forma en que está diseñado lo hace extremadamente limitado en el área n. ° 2 (siendo el n. ° 3 una consecuencia directa). Algunas de estas posibilidades limitadas se enumeran en otras respuestas, por ejemplo, a través del uso más básico de los combinadores de niños y hermanos, uso inteligente de pseudo-clases dinámicas (que en realidad se relaciona con la condición n. ° 1), o una combinación de ambos .

El problema que se plantea en la pregunta, por otro lado, no puede resolverse utilizando lo que está actualmente disponible en Selectors por este motivo. La mayor parte de esto se reduce a la falta de un selector parental y / o un selector de hermanos anterior, los cuales pueden parecer características triviales, pero tienen ciertas implicaciones que los hacen difíciles de definir o implementar bien. En resumen:

  1. Sí, el estado de estos elementos se puede representar mediante selectores simples: div y [data-status~=finished] para el primero, y .blink y .spin para los dos últimos.

  2. El primer elemento se puede representar por section > div[data-status~=finished] , y los dos elementos del sujeto se pueden representar por section + section > .blink y section + section > .spin respectivamente. El problema es que no es posible escribir un selector complejo que incorpore todas estas estructuras, porque los combinadores son unidireccionales, y no existe una contrapartida parental para el combinador infantil para unirlos en el primer elemento de section .

  3. Suponiendo que la respuesta a las dos primeras preguntas también es “sí”, cada uno de los elementos .blink y .spin puede convertirse en el tema de su propio selector complejo. (Pero más sobre eso en la próxima sección).

Si se le ha dirigido a esta pregunta, es probable que el problema que intenta resolver, como el anterior, no pueda resolverse con Selectores debido a estas limitaciones.

La próxima norma cuenta con algunas características nuevas que enriquecerán enormemente la syntax del selector y potencialmente la abrirán (y CSS) hasta un conjunto de nuevas posibilidades, incluida una posible solución al problema de ejemplo. Todas estas cosas se tratarán en las siguientes secciones, pero primero explicaré qué significa cada condición y cómo se relaciona con el ejemplo dado:

Estados de elementos y relaciones estructurales entre elementos

La característica definitoria de un selector es que representa una cierta estructura de uno o más elementos en el árbol de documentos. Esto no es solo algo que inventé; en realidad, puedes encontrar esta descripción en la visión general informativa de la norma Selectores :

Un Selector representa una estructura. Esta estructura se puede usar como una condición (por ejemplo, en una regla CSS) que determina qué elementos coincide un selector en el árbol del documento, o como una descripción plana del fragmento HTML o XML correspondiente a esa estructura.

Los selectores pueden variar desde nombres simples de elementos hasta representaciones contextuales ricas.

Cada elemento está representado por una secuencia de uno o más selectores simples. Esta secuencia se conoce como un selector compuesto (estoy usando la terminología de Selectores 4 aquí porque es mucho más clara que la que se usa en los Selectores 3 – vea esta respuesta para una lista no exhaustiva de términos).

Cada selector simple representa un cierto estado de un elemento. Hay selectores simples para hacer coincidir el tipo (o nombre de la etiqueta) de un elemento, un nombre de clase, una ID o un atributo arbitrario. También hay pseudo-clases, que representan abstracciones y otros estados especiales no representados directamente dentro del árbol de documentos, como el orden y la posición de un elemento en su jerarquía ( :nth-child() :nth-of-type() ), las interacciones del usuario ( :hover ,: active,: focus,: :checked ), la visita de un hipervínculo ( :link ,: :visited ) y mucho más.

En el ejemplo dado, el elemento div con un atributo de data-status cuyo valor delimitado por espacios contiene finished se puede representar con un selector de tipo y un selector de atributo:

 div[data-status~=finished] 

Si desea que el selector se aplique solo cuando el puntero esté sobre este elemento, simplemente agregue una :hover :

 div[data-status~=finished]:hover 

Los selectores compuestos están vinculados a través de combinadores para formar selectores complejos. Estos combinadores, los símbolos > , + y ~ que puede estar familiarizado, expresan una relación entre los elementos representados por cada selector compuesto. Con estas dos herramientas solo, ya puede crear algunos resultados muy interesantes, como se muestra en las otras respuestas aquí. Explico estos conceptos básicos con mayor profundidad en esta respuesta .

En el ejemplo dado, se pueden establecer las siguientes relaciones estructurales:

  • El primer elemento de section es el padre de div[data-status~=finished] . Esto se representa utilizando el combinador infantil > :

     section > div[data-status~=finished] 
  • La segunda section sigue inmediatamente a la primera como su hermano. Esto se representa usando el combinador de hermanos adyacente + :

     section + section 
  • Además, la segunda section es el padre de ambos .blink y .spin . Esto se puede representar usando dos selectores, uno para cada niño:

     section + section > .blink, section + section > .spin 

    ¿Por qué se requieren dos selectores? En este caso, es principalmente porque actualmente no hay syntax para agrupar dos selectores compuestos en uno , por lo que deberá representar cada elemento secundario por separado. El próximo estándar de Selectors 4 presenta una :matches() que proporcionará esta misma funcionalidad de subgrupos:

     section + section > :matches(.blink, .spin) 

Ahora, dado que cada selector compuesto en un selector complejo representa un elemento, y así la section + section representa dos elementos que son hermanos, la section > div representa un padre y un hijo, y la section + section > div representa un hijo de un próximo hermano , uno pensaría que un combinador de padres y un combinador de hermanos anteriores son bastante redundantes. Entonces, ¿por qué comúnmente obtenemos estas preguntas?

  • ¿Hay un selector de padres CSS?
  • ¿Hay un selector de CSS “hermano anterior”?

Y, lo que es más importante, ¿por qué la respuesta a ambas preguntas no es ? El motivo se aborda en el siguiente punto:

Objeto de un selector

El tema de un selector siempre está representado por el selector compuesto situado más a la derecha. Por ejemplo, la section + section > div selector section + section > div representa tres elementos, de los cuales div es el sujeto. Podría decir que se seleccionó el div , o se lo seleccionó , como en la pregunta, pero si alguna vez se ha preguntado si existía un término adecuado, se lo conoce como el tema del selector.

En una regla de CSS, los estilos se aplican al elemento representado por el sujeto del selector. Todos los cuadros secundarios y cuadros de pseudo-elemento heredan los estilos de este elemento cuando corresponda. (La excepción es si el tema del selector incluye un pseudo-elemento, en cuyo caso los estilos se aplican directamente al pseudo-elemento solamente).

Tomando los selectores de la sección anterior, tenemos lo siguiente:

  • El tema de la section > div[data-status~=finished] es div[data-status~=finished] .
  • El tema de section + section es el segundo selector de section .
  • Los temas de section + section > .blink, section + section > .spin son .blink y .spin respectivamente.
  • Usando :matches() , el tema de la section + section > :matches(.blink, .spin) es :matches(.blink, .spin) .

Por lo tanto, podría parecer que necesitamos un selector principal o un selector de hermanos anteriores. Pero recuerde que los selectores ya pueden representar estructuras complejas. En lugar de simplemente agregar nuevos combinadores que funcionen de manera opuesta a los existentes, tiene sentido buscar una solución más flexible, y eso es exactamente lo que el CSSWG ha estado haciendo.

Lo que nos lleva a lo siguiente de la pregunta original:

¿Hay un selector de CSS que me permita especificar qué elementos deberían seleccionarse en función del estado del elemento objective?

La respuesta a esto es no, y seguirá siendo no. Sin embargo, en los borradores anteriores de Selectors 4 (desde el FPWD hasta el último borrador en funcionamiento de mayo de 2013 ), hubo una propuesta para una nueva característica que le permitiera seleccionar cualquiera de los selectores compuestos que no sean el más a la derecha, y designar eso como el tema del selector.

Una solución potencial

Sin embargo, el indicador sujeto fue removido recientemente a favor de :has() pseudo-clase (que a su vez fue adoptado de jQuery ). Yo especulo sobre una posible razón aquí :

La razón :has() es más versátil es porque, con el selector de temas, nunca se dejó en claro en ningún borrador si un único selector complejo podría tener más de un selector de materias (ya que un solo selector complejo solo puede tener un tema) y / o si las pseudo-clases funcionales tales como :matches() aceptaron el selector de sujeto. Pero como una pseudoclase es un selector simple, usted sabe que :has() puede aceptarse en cualquier lugar donde se acepte una pseudoclase.

Entonces, si bien no puede cambiar el tema de un selector,: :has() borrará por completo la necesidad de hacerlo, debido a su naturaleza de pseudoclase. Y la mejor parte es que hace esto, y algo más , todo sin cambiar fundamentalmente la syntax del selector.

De hecho, el problema de ejemplo se puede resolver usando Selectors 4’s :has() :

 /* Combined with the :matches() example from above */ section:has(> div[data-status~=finished]) + section > div:matches(.blink, .spin) 

Observe el uso de un combinador infantil: este scope el argumento del selector relativo solo para los niños de la primera section . Sí, este es el evasivo “selector de padres” que los desarrolladores web de todo el mundo han estado esperando durante años.

Y como :has() viene de jQuery, puedes usarlo hoy , aunque :matches() aún no existe, así que tendrás que reemplazar eso con una llamada a .filter() mientras tanto:

 $('section:has(> div[data-status~=finished]) + section > div') .filter('.blink, .spin') .css('color', 'red'); 
  
Element 1
Element 2
Element 3
Element 4
Element 4
Element 4
Element 4
...

Estás muy limitado en lo que puedes lograr con el estado actual de CSS.

En resumen: puede hacer que los elementos de CSS reaccionen ante un cambio de estado de un elemento, si tienen el mismo padre Y son hermanos o hijos del padre.


El estado de un elemento en CSS se maneja mediante pseudo-clases , que cubren la mayoría de las interacciones típicas que maneja un navegador en función de la entrada del usuario.

Si bien esto le permite manejar el aspecto visual del estado actual de un elemento y sus elementos secundarios en el árbol DOM, aún no puede hacer que otros elementos no secundarios reaccionen (con un cambio visual de estilo) al estado actual de su elemento. elemento, ya que CSS no ofrece un tipo específico de selector para hacerlo de una manera flexible.

Sin embargo, puede combinar las pseudo-clases con otros tipos de selectores de CSS y hacer que esto funcione en ciertas situaciones (usaré el estado de desplazamiento porque es el más obvio):

pseudo clase + selector de hermanos adyacentes

El selector de hermanos adyacentes coincide si element1 y element2 comparten el mismo elemento primario en el árbol de documentos y element1 precede inmediatamente a element2 . ( Especificación W3C de selectores de hermanos adyacentes )

 div:hover + div { background:red; } 
 Hover on elements: 
Element 1
Element 2
Element 3
Element 4
Element 5

Como @easwee ya ha publicado una buena respuesta, no repetiría los selectores de pseudo-clase discutidos por él,


Una nueva pseudoclase que tenemos ahora, en css3 es : objective .

El pseudo selector de :target en CSS coincide cuando el hash en la URL y el id de un elemento son los mismos.

(El uso específico de: objective es elemento de estilo que está dirigido y curruntly visible en la parte superior de la ventana gráfica)

Por lo tanto, esto se puede usar (incorrectamente), en función de la interacción del usuario (haga clic específicamente), para cambiar otros estilos de elementos, cuando se usa con otras pseudo clases o selectores hermanos.

Por ejemplo: objective hijo de hermano de padre.

 :target section div[data-status=finished] { color: red; } a, a:visited { text-decoration: none; color: #000; } section { border: 1px solid grey; }