soy Kseso y esto EsCSS

Parent selector - selector de precedente o ancestro. O de cómo remontar el DOM con puro Css

Cómo actuar con puro Css sobre elementos precedentes en el DOM. Ya sean hermanos, padres o abuelos. Varias formas de emular el nonato selector de ancestros o "parent selector"

Parent selector - selector de precedente o ancestro. O de cómo remontar el DOM con puro Css

·Por Kseso ✎ 2
Parent selector - selector de precedente o ancestro. O de cómo remontar el DOM con puro Css
Lo que la especificación no da, la imaginación nos presta.

Es una máxima que debería presidir todas tus acciones. Y hablando de Css más que una frase bonita (y efectiva) debería ser una "condición sin la cual no" se debería permitir escribir ni un comentario en la hoja de estilo. En serio. Y para ilustrarlo nada mejor que enfrentarse a un "falso imposible" Css: cómo remontarse en el DOM o crear selectores de ancestro (parent selector) a la carta.

Un poco de historia

En los primeros borradores del documento "Selectors Level 4" apareció un nuevo combinador de selectores Css (el de antecesor) junto a los ya conocidos. A saber:

  1. De descendiente general (el espacio en blanco entre sos selectores simples): div p
  2. De hijo directo (>): nav > a
  3. De hermano posterior adyacente (+): h1 + p
  4. De hermano posterior general (~): p ~ ul
  5. Combinador de referencia
  6. Psudoclase :has() selector de ancestro

Pero por desgracia el selector de ancestro desapareció muy pronto y con ello su implementación en los navegadores. Posteriormente volvió a aparecer con una descripción y nomenclatura totalmente distintas. Así que a estas alturas (Marzo de 2015) seguimos con las ganas y a la espera de que sea una realidad usable.

Consulta el post El selector Css. Guía a fondo para iniciados y novatos en Css para ampliar este apartado.

Qué es el "parent selector"

Podríamos decir que el selector de ancestro o precedente sería aquél que permita actuar sobre un elemento anterior (o que precede) en el DOM del html al elemento que usamos como referente para apuntar a él.

Es hacer justo todo lo contrario de lo que ahora realizan todos los selectores Css basados en combinadores. Siempre se selecciona uno posterior ya sea por estar contenido (por ser descendiente) o aparecer después (hermano posterior).

Así, por ejemplo, la especificación no provee un combinador para actuar sobre una lista (ul) a través de sus items (li), para por ejemplo cambiar el borde en función de que hagamos :hover sobre el tercer li o sobre el quinto.

Remontando el DOM con puro Css

Sin embargo y pese a lo no existencia de un combinador para acceder a elementos anteriores del DOM, sí que es posible hoy día hacerlo o simularlo. Ambas acciones son posibles. La emulación (que aparentemente suceda algo que no es) y el remonte o retroceso en el DOM.

Para lo segundo, retroceder en el árbol del html y acceder a los ancestros tenemos, al menos, dos vías:

  1. La pseudoclase :target
  2. La pseudoclase :checked

:checked como parent selector

Aprovechando la posibilidad de separar los inputs de sus labels (ya explicado en otros post y usado para varias demos del blog) sólo necesitamos colocar los input´s al inicio de nuestro código html y sus label´s asociadas allí donde necesitemos, sin importar la profundidad a la que se encuentren.

Tal como en el siguiente pseudo código html:

<!-- todos los inputs --> <input uno> <input dos> ... <input enésimo> <!-- otro contenido --> <section> <article> <!-- contenido article --> <article> <section> <aside> <nav> <label for uno/> <label for dos/> <label for enésimo/> </nav> </aside>

Fíjate que en el "pseudocódigo" anterior los label´s (que son los elementos usados para interactuar) no sólo están después del article. Ni tan siquiera son hermanos. Unos son nietos de aside y el otro es hijo de sectión.

Por ello es por lo que hemos colocado los input´s al inicio de todo el html. Así podemos construir selectores basados en el :checked de cada input y el combinador de hermano general ~ para llegar al article:

#uno:checked ~ section > article { /* declaraciones oportunas */ } #dos:checked ~ * { /* declaraciones oportunas para todos sus hermanos y por herencia y/o cascada a sus descendientes /* }

Toda la teoría anterior aplicada a un ejemplo para cambiar tanto la familia tipográfica como el tamaño del texto (font-size) a todo el documento es la siguiente demo

See the Pen ::checked for parent selector by Kseso (@Kseso) on CodePen.

Ver demo a full

:target como selector de precedentes

La segunda opción para acceder vía Css a elementos precedentes en el árbol del html es utilizar la pseudoclase :target. O lo que es lo mismo, hacer uso de enlaces internos o anclas (anchors en inglés)

Con esta pseudoclase necesitamos crear el destino o bien utilizar algún elemento ya existente. Como por ejemplo, los elementos html y/o body añadiéndole sus id´s correspondiente.

También podemos optar por limpieza de código más un poco de imaginación para que el destino de :target sea el mismo elemento sobre el que actuamos.

Como los dos casos primeros (ids al 'html' y 'body' o anclas al inicio del documento apenas tienen alicientes, lo vemos con lo segundo: sencillez de código + imaginación.

Demos la oportunidad a nuestros visitantes de seleccionar una imagen de fondo de la página mediante unas miniaturas de la misma.
El marcado Html sería algo como:

<!-- Cualquier otro contenido --> <h2>Html selector: background</h2> <nav class='uno'> <a id='uno_1' href='#uno_1'></a> <a id='uno_2' href='#uno_2'></a> <a id='uno_3' href='#uno_3'></a> <a id='uno_4' href='#uno_4'></a> </nav>

El usar así el :target tiene un viejo problemilla: el salto inherente a él. Si eso fuese un problema, sólo tienes que añadir por cada enlace un span con su id y ocultarlo con display: none y ser este span el destino del enlace.

La intención es clara y la forma de lograrlo es sencilla. Al momento de apuntar a cada enlace (:target) usaremos su pseudoelemento para que muestre la imagen cubriendo todo el viewport. Para ello lo primero que tenemos que hacer es evitar que tanto el propio enlace como su padre ('nav') sean referentes del pseudoelemento por partida doble. Tanto para su ubicación como capa de pila (contexto de apilamiento para el z-index). Para ello la declaración position: static.

Y para que la imagen seleccionada cubra toda la ventana a la perfección en cualquier circunstancia o tamaño (sin zonas en blanco y sin necesidad de aumentarla de forma innecesaria) están la unidades relativas al viewport (vw | vh)

a:nth-child(1) {background-image: url(una.jpg);} a:nth-child(2) {background-image: url(dos.jpg);} a:before { content: ''; background: inherit; position: fixed; width: 0; height: 0; opacity: 0; transition: 1s linear; pointer-events: none; } a:target:before { width: 100vw; height: 100vh; z-index: -1; opacity: 1; }

Con lo anterior y algo más por aquello del jogo bonito puedes seleccionar la imagen que más te guste en la siguiente demostración:

See the Pen :target for parent selector by Kseso (@Kseso) on CodePen.

Ver demo a full

Emulando el selector de precedente

La segunda forma que te mencionaba es simular el funcionamiento del selector de precedente. Esto es, aparentar que al actuar sobre un elemento cambiamos valores de su padre o de hermanos que están antes de él en el DOM.

Un ejemplo de cómo modificar hermanos (anteriores y posteriores) ya lo expliqué hace un tiempo en este artículo.

Y una aplicación un poco más elaborada en base a esta técnica de disminuir el tamaño de todos los hijos al hacer :hover sobre el padre y aumentarlo en el hijo que al mismo tiempo recibe el :hover es el post "Slider con bordes sesgados y transición animada". En ambos tienes una explicación detallada paso a paso.

See the Pen Slider with oblique border by Kseso (@Kseso) on CodePen.

Ver demo a full

Esta fórmula tiene un escenario en el que fallará: si el padre tiene espacios no cubiertos por sus hijos. Como un padding o márgenes entre los hijos. Además, si existen esos espacios vacíos y queremos cambiar otros aspectos como podría ser mostrar un fondo o bordes en el padre distintos en función de qué hijo reciba la acción tampoco sería efectivo.

Para cada necesidad se requiere una solución expresa. Así que en este último supuesto (cambio de fondo y bordes) deberíamos emplear una aproximación diferente.

En este supuesto, y como demo final, podemos emplear algo parecido a lo usado con :target y limitado al padre: utilizar un pseudoelemento de cada item para que actúe como si del padre se tratase.

See the Pen 3º parent selector pure css by Kseso (@Kseso) on CodePen.

Ver demo a full

Epílogo

En este artículo el epílogo no puede ser otro que el prólogo:

Lo que la especificación no da, la imaginación nos presta.

Sólo necesitas parar un momento a pensar el resultado final que buscas y echarle algo de imaginación en el caso de que la especificación no te ofrezca una herramienta o vía directa para lograrlo. Es todo lo que se necesita para que los imposibles Css sólo sean meros juegos y divertimentos.

Y ahora sí, todos los ejemplos juntos por si hay quien así los quiera:

See the Pen 4 "parent selectors" pure Css by Kseso (@Kseso) on CodePen.

Ver demo a full o Descargar demo

avatar del Editor del blog

the obCSServer ᛯ Ramajero Argonauta, Enredique Amanuense de CSS.
#impoCSSible inside
Dicen que, en español, EsCss es el mejor blog de CSS. Posíblemente exageren.
@Kseso EsCss Kseso