Trucsweb.com

Trucsweb 1997-2017 - 20 ans de partage.

Javascript

Composants Web

RDFFav

Peut on enfin utiliser le Shadow DOM 1.0 ?

Oui, avec un bon vieux iFrame pour palier l’imcompatibilité total de IE et de Edge. Car ce second DOM caché et encapsulé à l’intérieur du DOM principal est des plus attendu.Shadow DOM, Web Components, ShadowRoot.host, ShadowRoot.mode, attachShadow(), template, dom.webcomponents.shadowdom.enabled, css

Shadow DOM (Source Chvad SB)

C’est en travaillant sur un projet d’éditeur de contenu en ligne que je suis tombé sur le texte de Chris Coyier, Playing with Shadow DOM (CSS-Tricks). Le texte soulignait l’initiative de Twitter d’utiliser le Shadow DOM pour pousser son contenu.

Le « Trick » étant de servir un Shadow DOM aux navigateurs compatibles (Chrome et Opera) et un bon vieux iFrame aux autres retardataires, et j’ai particulièrement nommé Internet Explorer et même Edge.

« Twitter appuie sa décision sur une meilleure utilisation de la mémoire par le navigateur et un temps de rendu beaucoup plus rapides. C’est à dire des tweets plus rapidement et un défilement plus doux. »

La version 1.0 fonctionne sous safari à part quelques sélecteurs :host > .local-child et ::slotted. Firefox v63+ supporte le Shadow DOM 1.0 mais il faut l’activer, question de sécurité !!

  1. Entrer dans l’adresse : about:config
  2. Rechercher la propriété : dom.webcomponents.enabled
  3. Activer la propriété : dom.webcomponents.enabled ou dans mon cas dom.webcomponents.shadowdom.enabled
Activer Shadow DOM avec Firefox

Version 1.0 car il y avait la version zéro ; -))

Mais oubliez la tout de suite, moins performant et surtout très peu supporté. Comparer la création d’un Shadow DOM :

// v0
let e = document.createElement('div');
let shadowRoot = e.createShadowRoot();

// v1
let e = document.createElement('div');
let shadowRoot = e.attachShadow({ mode: 'open' });
Styles délimités dit « scoped »
Éléments personnalisés inutiles

Les « éléments personnalisés » sont vraiment cutes, mais aussi complètement inutiles. En fait pour s’accorder avec la doctrine sémantique (;-), un élément personnalisé valable devrait de facto être transformé en balise officielle par la W3C !

Par contre, le modèle ou le gabarit (Templates), déjà plus compatible, peut devenir très pratique pour un développeur. Du simple stockage de donnée à l’encapsulation d’application des plus sophistiqués. Mon prochain tutoriel...

Personnellement (et pour une fois), l’optimisation de la performance au niveau du navigateur n’est pas ma principale motivation. Mon problème et celui de la plupart des développeurs, c’est le contenu tiers. Particulièrement son intégration au DOM sans toute fois partager ces propriétés et ses classes CSS. Le JavaScript permet d’intégrer un contenu distinct. Mais pour l’isoler complètement du DOM, il n’y a que la solution du iFrame. Avec tous les inconvénients que cela implique. Bon, inconvénients pour la plupart réglés depuis le temps, comme la hauteur ou les proportions d’un iFrame, mais ils n’empêchent que cette vielle méthode jure en boyenne dans la logique du Document Object Model (DOM) !

Un peu à la manière de feu l’attribue <style scoped>, supprimé des spécifications de la W3C (même les navigateurs qui le supportaient l’ont retiré). C’est-à-dire la possibilité d’appliquer des styles délimités à un bloc du DOM. Par exemple dans mon cas : une feuille de style particulière à mon éditeur HTML indépendante du style du SGC qui l’utilise.

En plus du Shadow DOM et du iFrame toujours obligatoire, cette technique ce présente souvent avec d’autres éléments : Les « éléments personnalisés » (Custom Elements) et les « Modèles » (Templates). Mais sachez qu’ils ne sont pas obligatoires dans le cadre de ce tutoriel.

À quoi ça sert ?

Je ne suis pas d’accord avec la définition de MDN à savoir « ...si le CSS n’est pas correctement organisé, les styles de certains composants peuvent impacter d’autres parties du site bien que ce ne soit pas prévu, et vice-versa. ». ...si le CSS n’est pas correctement organisé !! Une technique pour les mauvaises programmeur ;- ) Par contre le contenu d’un widget Facebook ou Twitter est totalement imprévisible. Imaginez les gars essayer de créer un code compatible avec le style de chaque site Web. De là l’usage du iFrame, toujours indépendant aux styles globaux du site.

Exemple d’arborescence du Shadow DOM

Le Shadow DOM fait exactement la même chose, mais en intégrant parfaitement, et de façon totalement indépendante, sa branche dans l’arbre du DOM. C’est-à-dire qu’on peut ensuite le manipuler à volonté et de façon dynamique, ajouter des nœuds. des évènements, modifier le contenu, le style, etc. Par exemple récupérer le contenu d’un Shadow DOM :

// On accède toujours au Shadow par sa racine : shadowRoot
document.getElementById("monobjetshadow").shadowRoot.innerHTML;
Créer un objet « Shadow »

Un Shadow DOM doit toujours être lié à un élément existant ou produit par un script dit l’« hôte ». L’hôte doit être membre du DOM. Si l’on pouvait utiliser la plupart des balises avec la version 0, par exemple « input » !! La version 1 limite ce choix, avec raison :

  • article
  • aside
  • blockquote
  • body
  • div
  • footer
  • h1
  • h2
  • h3
  • h4
  • h5
  • h6
  • header
  • main
  • nav
  • p
  • section
  • span
  • Plus les éléments personnalisés (Custom Elements).

Pour attacher le Shadow DOM à l’élément hôte il suffit de faire Element.attachShadow() comme dans cet exemple :

<div id="oContenu_isole">Votre navigateur ne supporte pas le « Shadow DOM »</div>
<script>
  // Créer le Shadow DOM sur l'élément <div>
  var shadow = document.querySelector('#oContenu_isole')
    .attachShadow({mode: "open"})
    .innerHTML = '<span">Contenu isolé du DOM principal !</span>'
  ;
</script>
Avertissement

Pour une compatibilité avec Chrome 34 et moins, vous devez préfixer le createShadowRoot avec « webkit ». Tous les appels à createShadowRoot devraient être host.webkitCreateShadowRoot().

Speak White

Mais attention, n’est pas compatible qui le veut. Au moment d’écrire ce texte, mon exemple ne fonctionnait pas ! Avant de réaliser que j’avais un caractère, ou plutôt deux caractères tellement illégaux qu’ils provoquaient une erreur. Je n’ai pas testé les milliers de caractères UTF-8, mais chose sûr, les caractères « et » font bizarrement planter ce JavaScript ! Ha les guillemets !

L’exemple ci-dessus ajoute un « Shadow DOM » à l’élément hôte <div> puis ajoute un nouveau contenu. Pourquoi « open » ? Pour rendre accessible le contenu du Shadow à partir de sa racine (shadowRoot) en mode « close » shadowRoot retourne « null ». C’est pourquoi on utilise pratiquement toujours « open ».

L’exemple suivant est le même, mais il utilise sa propre feuille de style exclusivement dédié au <div>. C’est-à-dire la raison première de son utilité :

<div id="oContenu_isole">Votre navigateur ne supporte pas le « Shadow DOM »</div>
<script>
  // Créer le Shadow DOM sur l'élément <div>
  var oShadow = document.querySelector('#oContenu_isole')
    .attachShadow({mode: "open"})
    .innerHTML = '<style>div {color:red;}</style><span>Contenu isolé du DOM principal en rouge !</span>'
  ;
</script>
Styles et sélecteurs CSS
Pseudo-classes CSS
  • defined
  • host
  • host()
  • host-context()
Styliser l’élément Shadow Root à partir de la page parent

L’élément hôte peut facilement être stylisé n’importe où dans la page parent, car l’élément hôte ne fait pas partie du Shadow DOM. Mais comment modifier le style de cet hôte en fonction du contenu du Shadow DOM ? Il existe un nouveau sélecteur « host » qui permet d’accéder à l’hôte du Shadow DOM depuis sa racine :

<style>
  :host {
    background-color:#ffffff;
  }
</style>

Le pseudo-éléments ::shadow permet de cibler la racine du Shadow DOM et ses éléments enfants. Par exemple ici les paragraphes :

<style>
  #host::shadow p {color: #555555;}
</style>

Pour appliquer un thème à une application Shadow DOM entière, utiliser le drôle de combinateur >>> (anciennement /deep/). Le combinateur >>> permet de croiser toutes les racines d’ombre avec un seul sélecteur : (NOTE - Inutile avec la version 1...)

<style>
  body >>> p {
    color: blue;
  }
</style>
Styliser l’élément Shadow Root à partir du Shadow Root

Les styles hérités héritent toujours des styles du DOM global. C’est pourquoi l’isolement n’est pas aussi efficace que le iFrame. Il faut donc obligés le Shadow DOM à revenir à l’état initial. En reprenant mon exemple :

<div id="oContenu_isole">Votre navigateur ne supporte pas le « Shadow DOM »</div>
<script>
  // Créer le Shadow DOM sur l’élément <div>
  var oShadow = document.querySelector('#oContenu_isole')
    .attachShadow({mode: "open"})
    .innerHTML = '<style>:host {all: initial;}</style><span>Contenu isolé du DOM principal en rouge !</span>'
  ;
</script>
Éditeur visuel

Maintenant on peut y mettre tout ce que l’on désire. Du plus complexe JavaScript à la plus banale petite classe CSS. Dans mon exemple, j’ai besoin de donner un aperçu du code saisi dans un textarea. Bien entendu je ne veux pas qu’il soit impacté par le CSS global de la page, mais en plus je désire lui appliquer le style perso du site sur lequel cette page sera affichée. Bien entendu, il n’est malheureusement pas question de charger une feuille de style externe à partir d’un autre domaine. Ça reste une simulation, mais tout à fait acceptable.

Imaginez, mon CMS Neural utilise un texte blanc sur un fond foncé. En conséquence et en tenant compte de l’héritage des styles globaux, si j’affiche le texte saisi dans un <div> neutre avec un fond blanc, le texte héritera de la couleur blanche globale et sera donc invisible sur le fond blanc ! Dans un iFrame (ou un popup) le style global de la page parente ne s’applique pas alors le problème ne se pose pas. J’utilise donc un Shadow DOM pour forcer la bonne couleur.

Bien sûr on peut utiliser des sélecteurs bien précis pour l’ensemble de la page limité à un objet (#MonObjet) et ainsi les isoler d’un possible contenu externe. Par exemple un objet #MonObjet p {...}, #MonObjet .maClasse {...}. Mais encore faut-il que ce tiers contenu soit déclaré à l’extérieur de l’objet en question. Ce qui limite les choix. Et surtout c’est pratiquement impossible. Les balises natives du HTML global gardent toujours leur propriété respective.

let sStyleTiers = `<style>body, p, td, li, div {color:red;}</style>`;

oShadow.innerHTML = sStyleTiers+' Contenu isolé du DOM principal en rouge';
L’indispensable iFrame

Si vous utilisez Internet Explorer ou un vieux navigateur, Twitter vous poussera quand même un iFrame au lieu du Shadow DOM. Et c’est là, à l’instar de Twitter, que la possibilité d’utiliser cette nouvelle technologie devient possible et très intéressante. Obtenir les capacités du Shadow Dom, sinon, au pire, utiliser le bon vieux iFrame. Alors, pourquoi s’en priver?

<div id="oContenu_isole"><p>Votre navigateur ne supporte pas le « Shadow DOM » !</p></div>

<style>
iframe {border: 0;width: 100%;padding: 0;}
</style>

<script>
let sStyleTiers = `
  <style>
    :host {all:initial;} 
    body, p, td, li, div {color:red;}
  </style>
  <p>Votre navigateur supporte le Shadow DOM !</p>
`;

let el = document.querySelector('#oContenu_isole');
if (document.body.attachShadow) {
  // Si compatible Shadow DOM
  let oShadow = el.attachShadow({ mode: 'open' });
  oShadow.innerHTML = sStyleTiers;
} else {
  // Sinon utiliser un iFrame
  let oFrame = document.createElement('iframe');
  oFrame.id = 'oFrame';
  'srcdoc' in oFrame ?
    oFrame.srcdoc = sStyleTiers :
    oFrame.src = 'data:text/html;charset=UTF-8,' + sStyleTiers;
  // Et on remplace le DIV par le iframe
  let parent = el.parentNode;
  parent.replaceChild(oFrame, el);
}
</script>

L’exemple permet, dans le cas d’incompatibilité avec le Shadow DOM, de créer à la volé un iFrame et de le remplir avec le contenu. Puis de simplement remplacer le DIV du Shadow par le iFrame en question. Avec le petit fixe de Šime Vidas. Vous pouvez aussi attacher une feuille de styles au iFrame :

Conclusion

Ce tutoriel ne trace que les grandes lignes de cette technique. Plusieurs aspects n’ont pas été traités, par exemple : DocumentOrShadowRoot, template, content, slot et les sélecteurs ::slotted, :host-context(), :host().

Références
, Analyste programmeurConception oznogco multimédia (http://oznogco.com), Trucsweb
Dernière mise à jour :

Commentaires

8/10 sur 1 revues.
       Visites : 172 - Pages vues : 210
X

Trucsweb.com Connexion

X

Trucsweb.com Mot de passe perdu

X

Trucsweb.com Conditions générales

Conditions

Responsabilité

La responsabilité des Trucsweb.com ne pourra être engagée en cas de faits indépendants de sa volonté. Les informations mises à disposition sur ce site le sont uniquement à titre purement informatif et ne sauraient constituer en aucun cas un conseil ou une recommandation de quelque nature que ce soit.

Aucun contrôle n'est exercé sur les références et ressources externes, l'utilisateur reconnaît que les Trucsweb.com n'assume aucune responsabilité relative à la mise à disposition de ces ressources, et ne peut être tenue responsable quant à leur contenu.

Droit applicable et juridiction compétente

Les règles en matière de droit, applicables aux contenus et aux transmissions de données sur et autour du site, sont déterminées par la loi canadienne. En cas de litige, n'ayant pu faire l'objet d'un accord à l'amiable, seuls les tribunaux canadien sont compétents.

X

Trucsweb.com Trucsweb

X

Trucsweb.com Glossaire

X

Trucsweb.com Trucsweb

X

Trucsweb.com Trucsweb

Conditions

Aucun message!

Merci.

X
Aucun message!
X

Trucsweb.com Créer un compte