Catégories
Astuces et Design

Des moyens plus intelligents pour générer une structure HTML imbriquée profonde

Disons que nous voulons avoir la structure HTML suivante:

C’est vraiment pénible d’écrire manuellement. Et la raison pour laquelle ce message est né était horrifié de le voir généré avec Haml comme ceci:

.boo
  .boo
    .boo
      .boo
        .boo

Il y avait en fait une vingtaine de niveaux d'imbrication dans le code que j'ai vu, mais peut-être que certaines personnes lisent des choses sur un téléphone portable, alors ne remplissons pas toute la fenêtre avec des huées, même si Halloween est proche.

Comme vous pouvez probablement le constater, l'écriture manuelle de chaque niveau est loin d'être idéale, surtout lorsque le HTML est généré par un préprocesseur (ou à partir de JavaScript, ou même d'un langage back-end comme PHP). Personnellement, je ne suis pas fan de l'imbrication profonde et je ne l'utilise pas beaucoup moi-même, mais si vous y allez quand même, je pense que cela vaut la peine de le faire d'une manière qui s'adapte bien et soit facilement maintenable.

Jetons donc d'abord un coup d'œil à de meilleures solutions pour ce cas de base et ses variantes, puis voyons des choses amusantes réalisées avec ce type d'imbrication profonde!

La solution de base

Ce dont nous avons besoin ici, c'est d'une approche récursive. Par exemple, avec Haml, le bit de code suivant fait l'affaire:

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

Il y a une classe d'emoji là-dedans parce que nous pouvons et parce que ce n'est qu'un petit exemple amusant. Je n'utiliserais certainement pas de cours d'emoji sur un site Web réel, mais dans d'autres situations, j'aime m'amuser un peu avec le code que j'écris.

Nous pouvons également générer le HTML avec Pug:

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

+nest('👻', 5)

Ensuite, il y a aussi l'option 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)

Avec PHP, nous pouvons utiliser quelque chose comme ceci:

";
  if(--$n > 0) nest($cls, $n);
  echo "
"; } nest('👻', 5); ?>

Notez que la principale différence entre ce que chacun de ces produits produit est liée au formatage et aux espaces blancs. Cela signifie que cibler le "boo" le plus profond avec .👻:empty va fonctionner pour le HTML généré par Haml, JavaScript et PHP, mais échouera pour celui généré par Pug.

Ajout d'indicateurs de niveau

Disons que nous voulons que chacun de nos boos ait un indicateur de niveau en tant que propriété personnalisée --i, qui pourrait ensuite être utilisé pour donner à chacun d'eux un background, par exemple.

Vous pensez peut-être que si tout ce que nous voulons est de changer la teinte, nous pouvons le faire avec filter: hue-rotate() et se passer d'indicateurs de niveau. cependant, hue-rotate() n'affecte pas seulement la teinte, mais également la saturation et la luminosité. Il n'offre pas non plus le même niveau de contrôle que l'utilisation de nos propres fonctions personnalisées qui dépendent d'un indicateur de niveau, --i.

Par exemple, c'est quelque chose que j'ai utilisé dans un projet récent afin de faire background les composants changent en douceur d'un niveau à l'autre (le $c les valeurs sont des coefficients polynomiaux):

--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));

Ajuster le carlin pour ajouter des indicateurs de niveau se présente comme suit:

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

+nest('👻', 5)

La version Haml n'est pas trop différente non plus:

- def nest(cls, n, i = 0);
-   return '' unless i < n;
-   "
#{nest(cls, n, i + 1)}
"; end = nest('👻', 5)

Avec JavaScript, nous avons:

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)

Et avec PHP, le code ressemble à ceci:

";
  if(++$i < $n) nest($cls, $n, $i);
  echo "
"; } nest('👻', 5); ?>

Une structure plus arborescente

Disons que nous voulons que chacun de nos huées ait deux enfants boo, pour une structure qui ressemble à ceci:

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

Heureusement, nous n'avons pas besoin de beaucoup changer notre mix de base Pug pour obtenir ceci (démo):

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

+nest('👻', 5)

Il en va de même pour la version Haml:

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

La version JavaScript demande un peu plus d'efforts, mais pas trop:

function nest(_parent, cls, n) {
  let _el = document.createElement('div');
  
  if(n > 1) {
    nest(_el, cls, n);
    nest(_el, cls, n)
  }

  _el.classList.add(cls);
  _parent.appendChild(_el)
};

nest(document.body, '👻', 5)

Avec PHP, il suffit d'appeler le nest() fonctionner à nouveau dans le if bloquer:

";
  if(--$n > 0) {
    nest($cls, $n);
    nest($cls, $n);
  }
  echo "
"; } nest('👻', 5); ?>

Styliser différemment l'élément de niveau supérieur

Nous pourrions bien sûr ajouter un spécial .top (ou .root ou quelque chose de similaire) classe uniquement pour le niveau supérieur, mais je préfère laisser cela au CSS:

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

Fais attention!

Certaines propriétés, telles que transform, filter, clip-path, mask ou opacity n'affectent pas seulement un élément, mais également tous ses descendants. Parfois, c'est l'effet recherché et précisément la raison pour laquelle il est préférable d'imbriquer ces éléments plutôt que d'être des frères et sœurs.

Cependant, d'autres fois, ce n'est peut-être pas ce que nous voulons, et s'il est possible d'inverser les effets de transform et parfois même filter, nous ne pouvons rien faire pour les autres. Nous ne pouvons pas, par exemple, définir opacity: 1.25 sur un élément pour compenser le fait que son parent ait opacity: .8.

Exemples!

Tout d'abord, nous avons ce pur chargeur de points CSS que j'ai récemment créé pour un défi CodePen:

Ici, les effets des transformations de mise à l'échelle et des rotations animées s'additionnent sur les éléments internes, tout comme les opacités.

La prochaine étape est cette danse yin et yang, qui utilise la structure en forme d'arbre:

Pour chaque élément, sauf le plus externe (:not(.☯️) > .☯️), le diamètre est égal à la moitié de celui de son parent. Pour les objets les plus intimes (.☯️:empty, que je suppose que nous pouvons appeler les feuilles des arbres), le background a deux supplémentaires radial-gradient() couches. Et tout comme la première démo, les effets des rotations animées s'additionnent sur les éléments internes.

Un autre exemple serait ces tentacules de bonbons en rotation:

Chacun des anneaux concentriques représente un niveau d'imbrication et combine les effets des rotations animées de tous ses ancêtres avec les siens.

Enfin, nous avons cette démonstration d'ouvertures triangulaires (notez qu'elle utilise des propriétés de transformation individuelles telles que rotate et scale alors le Fonctionnalités de la plateforme Web expérimentale l'indicateur doit être activé dans chrome://flags afin de le voir fonctionner dans les navigateurs Chromium):

Cela utilise une version légèrement modifiée du mixin d'imbrication de base afin de définir également un color à chaque niveau:

- 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)

Ce qui s'anime ici, ce sont les propriétés de transformation individuelles scale et rotate. Ceci est fait afin que nous puissions définir différentes fonctions de synchronisation pour eux.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *