Composants Web Partie 3: Modèles HTML et importations

Entrée

Salutations à mes collègues. Cet article est le troisième et dernier article d'une série d'articles sur les composants Web. Les deux premiers articles sont disponibles à l'adresse suivante:


Composants Web. Partie 1: Éléments personnalisés
Composants Web Partie 2: Shadow DOM

Cet article parlera de l'élément <template> et également des importations HTML.



Élément de modèles HTML

L'élément <template> est un outil qui vous permet de stocker du contenu côté client sans le restituer à la page, mais avec la possibilité de l'afficher pendant l'exécution via JavaScript.


Lors de l'analyse d'une page, le contenu d'un élément est traité uniquement en termes de validation de contenu, mais sans le rendre (selon la spécification, cet élément ne représente rien lors du rendu). Le contenu de l'élément peut être cloné et collé dans le document à partir de scripts, qui est utilisé à la fois indépendamment pour la normalisation et lors de la création de composants Web.


Contenu <template>

Le contenu du <template>, ainsi que pour tout nœud qui n'a pas de contexte de navigateur, n'applique aucune exigence de conformité, à l'exception des exigences pour l'exactitude de la syntaxe HTML et XML. Cela signifie que dans le contenu du modèle, par exemple, vous pouvez spécifier l'élément img sans spécifier la valeur des attributs src et alt, ainsi:


<template> <div> <img src="{{src}}" alt="{{alt}}"> </div> </template> 

cependant, en dehors de l'élément <template>, une telle syntaxe n'est pas valide. Dans ce cas, ignorer la balise de fermeture </div> serait une violation de la syntaxe HTML et n'est pas valide pour le contenu de <template>.


Tous les éléments spécifiés à l'intérieur de la balise <template> dans le code html ne sont pas ses enfants.


Les navigateurs lors de la création d'un élément <template> créent un DocumentFragment dont le document est le soi-disant approuver le propriétaire du contenu du modèle, déterminé par cet algorithme , le document dans lequel <template> est spécifié et indique la valeur de la propriété .content créée par DocumentFragment.


Autrement dit, la propriété .content de l'élément de modèle contient un DocumentFragment, et les éléments qui ont été spécifiés dans le code html à l'intérieur des balises <template> sont des enfants de ce DocumentFragment.


Dans ce cas, l'élément <template>, comme tout autre, peut être ajouté avec des éléments enfants ( appendChild () ), mais cela sera considéré comme une violation du modèle de contenu du modèle.


Modèle de clonage

Lors du clonage du contenu d'un modèle, il est important de se rappeler que le premier argument de .cloneNode ([deep])
ou le second dans .importNode (externalNode, deep) doit être transféré (selon la spécification, si l'argument n'est pas passé, aucune autre exécution ne devrait avoir lieu).


Au fait, oui, malgré le fait que la plupart des exemples utilisent .cloneNode (), l'utilisation de .importNode () est également possible. La seule différence est lorsque le document est mis à jour (pour .cloneNode () - après avoir appelé appendChild (); pour .importNode () - après le clonage).


Montrez-moi le code ©

L'utilisation de modèles est vraiment très simple. Je vais continuer l'exemple des composants de l'onglet, avec le code dont j'ai travaillé dans les exemples d'articles précédents.


Je vais commencer par créer deux éléments <template> dans le balisage html et transférer le balisage qui s'y trouvait dans la méthode .render () des classes TabNavigationItem et TabContentItem (j'ai également changé certains styles, mais cela n'affecte pas la fonctionnalité):


  <template id="tab-nav"> <style> :host{ padding: 10px; background-color: rgb(81,180,186); transition: background-color 1s; text-align: center; } :host-context(.active) { background-color: rgb(93, 209, 216); } a{ text-decoration: none; color: rgb(3,32,40); } </style> <a href="#${this._target}"><slot></slot></a> </template> 

et:


  <template id="tab-content"> <style> :host { display: none; padding: 20px; width: 100%; background-color: rgb(255,212,201); } :host-context(.active){ display: block; } </style> <div><slot></slot></div> </template> 

Dans le constructeur de chaque classe, je vais enregistrer la propriété du modèle. Pour TabNavigationItem, ce sera:


  this.template = document.getElementById('tab-nav'); 

a pour TabContentItem:


  this.template = document.getElementById('tab-content'); 

Dans la méthode render () de chacune de ces classes, j'ajouterai le code suivant, après avoir supprimé l'entrée .innerHTML:


  const content = this.template.content.cloneNode(true); this.shadowRoot.appendChild(content); 

Le code résultant peut être trouvé ici.


Dans cet exemple, les deux modèles sont spécifiés en html, ce qui semble lourd et ne bourdonne pas. Cela nous amène en douceur au sujet:


Importations HTML

Les importations sont des documents HTML qui sont connectés en tant que ressources externes par un autre document HTML. Le système de relations entre les documents est bien décrit dans le projet de spécification et ne fait pas l'objet de cet article.

Le schéma général est visible dans l'image:


.

Afin d'implémenter les importations, un nouveau type a été ajouté aux types de liens HTML (valeurs de l'attribut rel).


Le mot d'importation spécifié dans la valeur de l'attribut rel de l'élément <link> lui-même crée un lien vers la ressource importée (le type par défaut de la ressource est text / html).


L'élément <link> peut avoir un attribut asynchrone.


Les extensions proposées par le projet de spécifications sont proposées dans l'API HTMLLinkElement: une propriété d'importation en lecture seule est ajoutée qui contient le document importé.


Une propriété peut contenir null dans deux cas: lorsque <link> ne représente pas l'importation ou <link> n'est pas dans le document.


La spécification indique séparément que le même objet doit toujours être retourné.


Dans le contexte des importations, il existe un document dit maître, qui est le document qui importe des ressources en même temps sans être la ressource importée de quelqu'un d'autre.

La ContentSecurityPolicy d'un tel document devrait limiter toutes les importations. Par conséquent, si le champ d'en-tête de sécurité du contenu est défini pour être importé, le navigateur doit appliquer la stratégie du document maître au document importé.


En pratique

Pour le composant onglet, je crée un dossier de modèles. Dans ce document, je vais créer deux fichiers dans lesquels je transférerai le balisage du composant.


  <!--templates/tab-content.html--> <template id="tab-content"> <style> :host { display: none; padding: 20px; width: 100%; background-color: rgb(255,212,201); } :host-context(.active){ display: block; } </style> <div><slot></slot></div> </template> <!--templates/tab-nav.html--> <template id="tab-nav"> <style> :host{ padding: 10px; background-color: rgb(81,180,186); transition: background-color 1s; text-align: center; } :host-context(.active) { background-color: rgb(93, 209, 216); } a{ text-decoration: none; color: rgb(3,32,40); } </style> <a href="#${this._target}"><slot></slot></a> </template> 

Dans la <head> du fichier index.html, j'importe les modèles:


  <link rel="import" href="templates/tab-nav.html" id="tab-nav"> <link rel="import" href="templates/tab-content.html" id="tab-content"> 

J'ajoute des attributs id aux éléments <link>, car je devrai y accéder depuis js.
Maintenant, dans les constructeurs des classes TabNavigationItem et TabContentItem, pour obtenir le document de modèle, il me suffit de trouver l'élément <link> correspondant et de me tourner vers sa propriété d'importation, après quoi je rechercherai le modèle déjà dans le document importé:


  class TabNavigationItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({mode: 'open'}); const templateImport = document.getElementById('tab-nav').import; this.template = templateImport.getElementById('tab-nav'); } //... } class TabContentItem extends HTMLElement { constructor() { super(); this._target = null; this.attachShadow({mode: 'open'}); const templateImport = document.getElementById('tab-content').import; this.template = templateImport.getElementById('tab-content'); } //... } 

La version finale peut être prise ici .


À propos du support

Prise en charge des modèles HTML: Edge c 16, Firefox c 59, Chrome c 49, Safari c 11.
Avec un support d'importation plus triste: Chrome c 49.
Par conséquent, les exemples de cet article ne sont visibles que dans la dernière version de Chrome.


Il existe des polyphiles:


Composants Web
Projet polymère

En savoir plus sur les modèles et les importations:

Spécification HTML
Spécification HTML5
Projet de spécifications HTML Imports

C'est tout, merci d'avoir regardé,
Tanya

Source: https://habr.com/ru/post/fr414905/


All Articles