Formas más inteligentes de generar una estructura HTML anidada profunda

Índice
  1. La solución básica
  2. Agregar indicadores de nivel
  3. Una estructura más parecida a un árbol.
  4. Diseñar el elemento de nivel superior de manera diferente
  5. ¡Cuidado!
  6. ¡Ejemplos!

Digamos que queremos tener la siguiente estructura HTML:

div class='boo'  div class='boo'    div class='boo'      div class='boo'        div class='boo'/div      /div    /div  /div/div

Es realmente complicado escribir manualmente. Y la razón por la que nació este post fue por el horror al verlo generado con Haml así:

.boo  .boo    .boo      .boo        .boo

En realidad, había unos veinte niveles de anidamiento en el código que vi, pero tal vez algunas personas están leyendo algo en un teléfono móvil, así que no llenamos toda la ventana gráfica con abucheos, incluso si Halloween está cerca.

Como probablemente podrás ver, escribir manualmente cada nivel está lejos de ser ideal, especialmente cuando el HTML es generado por un preprocesador (o desde JavaScript, o incluso un lenguaje de back-end como PHP). Personalmente, no soy un fanático del anidamiento profundo y no lo uso mucho, pero si lo intentas de todos modos, creo que vale la pena hacerlo de una manera que se adapta bien y sea fácil de mantener.

Así que primero echamos un vistazo a algunas soluciones mejores para este caso base y sus variaciones y luego veamos algunas cosas divertidas hechas con este tipo de anidamiento profundo.

La solución básica

Lo que necesitamos aquí es un enfoque recursivo. Por ejemplo, con Haml, el siguiente código funciona:

- def nest(cls, n);-  return '' unless n  0;-  "div class='#{cls}'#{nest(cls, n - 1)}/div"; end= nest(' ', 5)

Hay una clase de emoji porque podemos y porque este es solo un pequeño ejemplo divertido. Definitivamente no usaría clases de emoji en un sitio web real, pero en otras situaciones, me gusta divertirme un poco con el código que escribo.

También podemos generar el HTML con Pug:

mixin nest(cls, n)  div(class=cls)    if --n      +nest(cls, n)+nest(' ', 5)

Luego también está la opción de JavaScript:

function nest(_parent, cls, n) {  let _el = document.createElement('div');  if(--n) nest(_el, cls, n);  _el.classList.add(cls);  _parent.appendChild(_el)};nest(document.body, ' ', 5)

Con PHP, podemos usar algo como esto:

?phpfunction nest($cls, $n) {  echo "div class='$cls'";  if(--$n  0) nest($cls, $n);  echo "/div";}nest(' ', 5);?

Tenga en cuenta que la principal diferencia entre lo que produce cada uno de estos está relacionada con el formato y el espacio en blanco. Esto significa que apuntar al “boo” más interno . :emptyfuncionará para el HTML generado por Haml, JavaScript y PHP, pero fallará para el generado por Pug.

Agregar indicadores de nivel

Digamos que queremos que cada uno de nuestros abucheos tenga un indicador de nivel como propiedad personalizada --i, que luego podría usar para darle a cada uno de ellos un valor diferente background, por ejemplo.

Quizás estés pensando que, si lo único que queremos es cambiar el tono, entonces podemos hacerlo con filter: hue-rotate()y sin indicadores de nivel. Sin embargo, hue-rotate()no sólo afecta la tonalidad, sino también la saturación y la luminosidad. Tampoco proporciona el mismo nivel de control que usar nuestras propias funciones personalizadas que dependen de un indicador de nivel --i.

Por ejemplo, esto es algo que usé en un proyecto reciente para hacer que backgroundlos componentes cambien suavemente de un nivel a otro (los $cvalores son coeficientes polinomiales):

--sq: calc(var(--i)*var(--i)); /* square */--cb: calc(var(--sq)*var(--i)); /* cube */--hue: calc(#{$ch0} + #{$ch1}*var(--i) + #{$ch2}*var(--sq) + #{$ch3}*var(--cb));--sat: calc((#{$cs0} + #{$cs1}*var(--i) + #{$cs2}*var(--sq) + #{$cs3}*var(--cb))*1%);--lum: calc((#{$cl0} + #{$cl1}*var(--i) + #{$cl2}*var(--sq) + #{$cl3}*var(--cb))*1%);background: hsl(var(--hue), var(--sat), var(--lum));

Ajustar el Pug para agregar indicadores de nivel se ve de la siguiente manera:

mixin nest(cls, n, i = 0)  div(class=cls style=`--i: ${i}`)    if ++i  n      +nest(cls, n, i)+nest(' ', 5)

La versión de Haml tampoco es muy diferente:

- def nest(cls, n, i = 0);-   return '' unless i  n;-   "div class='#{cls}' style='--i: #{i}'#{nest(cls, n, i + 1)}/div"; end= nest(' ', 5)

Con JavaScript, tenemos:

function nest(_parent, cls, n, i = 0) {  let _el = document.createElement('div');  _el.style.setProperty('--i', i);  if(++i  n) nest(_el, cls, n, i);  _el.classList.add(cls);  _parent.appendChild(_el)};nest(document.body, ' ', 5)

Y con PHP, el código se ve así:

?phpfunction nest($cls, $n, $i = 0) {  echo "div class='$cls' style='--i: $i'";  if(++$i  $n) nest($cls, $n, $i);  echo "/div";}nest(' ', 5);?

Una estructura más parecida a un árbol.

Digamos que queremos que cada uno de nuestros abucheos tenga dos hijos abucheos, para una estructura que se ve así:

.boo  .boo    .boo      .boo      .boo    .boo      .boo      .boo  .boo    .boo      .boo      .boo    .boo      .boo      .boo

Afortunadamente, no tenemos que cambiar mucho nuestra mezcla base de Pug para obtener esto (demostración):

mixin nest(cls, n)  div(class=cls)    if --n      +nest(cls, n)      +nest(cls, n)+nest(' ', 5)

Lo mismo ocurre con la versión Haml :

- def nest(cls, n);-   return '' unless n  0;-   "div class='#{cls}'#{nest(cls, n - 1)}#{nest(cls, n - 1)}/div"; end= nest(' ', 5)

La versión JavaScript requiere un poco más de esfuerzo, pero no demasiado:

function nest(_parent, cls, n) {  let _el = document.createElement('div');    if(n  1) {    nest(_el, cls, n - 1);    nest(_el, cls, n - 1)  }  _el.classList.add(cls);  _parent.appendChild(_el)};nest(document.body, ' ', 5)

Con PHP, sólo necesitamos llamar a la nest()función una vez más en el ifbloque:

?phpfunction nest($cls, $n) {  echo "div class='$cls'";  if(--$n  0) {    nest($cls, $n);    nest($cls, $n);  }  echo "/div";}nest(' ', 5);?

Diseñar el elemento de nivel superior de manera diferente

Por supuesto, podríamos agregar una clase especial .top(o .rootalgo similar) solo para el nivel superior, pero prefiero dejar esto en manos del CSS:

:not(. )  .  {  /* Top-level styles*/}

¡Cuidado!

Algunas propiedades, como transform, filter, clip-path, masko opacityno sólo afectan a un elemento, sino también a todos sus descendientes. En ocasiones este es el efecto deseado y precisamente el motivo por el que se prefiere anidar estos elementos a que sean hermanos.

Sin embargo, otras veces puede que no sea lo que queremos y, si bien es posible revertir los efectos transformy, a veces, incluso filter, no hay nada que podamos hacer con respecto a los demás. No podemos, por ejemplo, establecer opacity: 1.25un elemento para compensar el hecho de que su padre tenga opacity: .8.

¡Ejemplos!

En primer lugar, tenemos este cargador de puntos CSS puro que hice recientemente para un desafío de CodePen:

Aquí, los efectos de las transformaciones de escala y de las rotaciones animadas se suman a los elementos internos, al igual que las opacidades.

El siguiente paso es esta danza del yin y el yang, que utiliza la estructura en forma de árbol:

Para cada artículo, excepto el más externo ( :not(.☯️) .☯️), el diámetro es igual a la mitad de su padre. Para los elementos más internos ( .☯️:empty, que supongo que podemos llamar hojas de árbol), backgroundtiene dos radial-gradient()capas adicionales. Y al igual que en la primera demostración, los efectos de las rotaciones animadas se suman a los elementos internos.

Otro ejemplo serían estos tentáculos de caramelo giratorios:

Cada uno de los anillos concéntricos representa un nivel de anidamiento y combina los efectos de las rotaciones animadas de todos sus antepasados ​​con los suyos propios.

Finalmente, tenemos esta demostración de aperturas triangulares (tenga en cuenta que utiliza propiedades de transformación individuales, rotatepor scalelo que el indicador de características de la plataforma web experimental debe estar habilitado para chrome://flagsverlo funcionar en los navegadores Chromium):

Esto utiliza una versión ligeramente modificada del mixin de anidamiento básico para establecer también un coloren cada nivel:

- let c = ['#b05574', '#f87e7b', '#fab87f', '#dcd1b4', '#5e9fa3'];- let n = c.length;mixin nest(cls, n)  div(class=cls style=`color: ${c[--n]}`)    if n      +nest(cls, n)body(style=`background: ${c[0]}`)  +nest(' ', n)

Lo que se anima aquí son las propiedades de transformación individuales scaley rotate. Esto se hace para que podamos configurar diferentes funciones de sincronización para ellos.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion