--- title: Utilisation des templates et des slots slug: Web/Web_Components/Using_templates_and_slots tags: - Composant web - HTML - Template - shadow dom - slot translation_of: Web/Web_Components/Using_templates_and_slots original_slug: Web/Web_Components/Utilisation_des_templates_et_des_slots ---
{{DefaultAPISidebar("Web Components")}}

Cet article explique comment vous pouvez utiliser les éléments {{htmlelement("template")}} et {{htmlelement("slot")}} pour créer un template flexible qui peut ensuite être utilisé pour alimenter le Shadow DOM d'un composant Web.

La vérité sur les templates

Lorsqu'une structure de balises se répète sur une page Web, il est judicieux d'utiliser un template plutôt que d'écrire cette même structure encore et encore. Il était déjà possible de le faire, mais l'élément HTML {{htmlelement("template")}} (bien supporté par les navigateurs modernes) nous facilite la tâche. Cet élément et ce qu'il renferme n'est pas directement retranscrit dans le DOM, mais peut par contre toujours être manipulé avec JavaScript.

Voyons un exemple simple:

<template id="my-paragraph">
  <p>My paragraph</p>
</template>

Le tag <template> et ce qu'il contient restera invisible sur la page tant qu'aucune référence n'y sera faite dans le code JavaScript puis ajouté au DOM, en utilisant par exemple:

let template = document.getElementById('my-paragraph');
let templateContent = template.content;
document.body.appendChild(templateContent);

Quoique trivial, cet exemple vous permet d'entrevoir l'interêt d'utiliser des templates.

Accorder templates et composants Web

Les templates sont utiles en eux-mêmes, mais ils fonctionnent encore mieux avec des composants Web. Créons un composant Web qui utilise notre template comme contenu de son Shadow DOM. Nous l'appellerons <my-paragraph>:

customElements.define('my-paragraph',
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById('my-paragraph');
      let templateContent = template.content;

      const shadowRoot = this.attachShadow({mode: 'open'})
        .appendChild(templateContent.cloneNode(true));
  }
})

Le point important à noter est que l'on ajoute un clone du contenu du template à la racine du DOM, créé à l'aide de la méthode {{domxref("Node.cloneNode()")}}.

Et parce que nous ajoutons son contenu à un Shadow DOM, on peut inclure des informations de mises en forme à l'intérieur du template dans d'un élément {{htmlelement("style")}}, qui est ensuite encapsulé à l'intérieur de l'élément personnalisé. Cette procédure n'aurait pas fonctionné si on avait ajouté le contenu à un DOM standard.

Par exemple:

<template id="my-paragraph">
  <style>
    p {
      color: white;
      background-color: #666;
      padding: 5px;
    }
  </style>
  <p>My paragraph</p>
</template>

On peut maintenent utiliser le template dans le document HTML:

<my-paragraph></my-paragraph>

Note: Les templates sont bien supportés par les navigateurs; l'API Shadow DOM est pris en charge par défaut dans Firefox (version 63 onwards), Chrome, Opera, et Safari. Edge travail également sur une implémentation.

Plus de flexibilité avec les slots

Jusque là, nous avons vu une première utilisation du tag template. Cette implémentation n'est pas très fexible; elle ne permet d'afficher que du texte, c'est à dire que son utilité est presque nulle! Il est possible d'insérer du texte dans chaque instance d'élément de façon déclarative grâce à {{htmlelement("slot")}}. Cette fonction est moins bien prise en charge que {{htmlelement("template")}}, disponible sur Chrome 53, Opera 40, Safari 10, Firefox 59, mais pas encore sur Edge.

Les slots sont identifiés par leur attribut name, et permettent de définir des champs dans le template qui peuvent être alimentés avec n'importe quelle structure HTML.

Donc, si on souhaite ajouter un slot dans le précédent exemple sur les templates, on peut modifier l'élément paragraphe de cette façon:

<p><slot name="my-text">My default text</slot></p>

Si le contenu du slot n'est pas défini quand l'élément est inclu dans la page, ou si les slots ne sont pas supportés par le navigateur, <my-paragraph>  contient simplement le texte statique précisé dans le template.

Pour définir le contenu du slot, on insère une structure HTML dans <my-paragraph> avec un attribut {{htmlattrxref("slot")}} dont la valeur est égale au nom du slot que l'on veut alimenter. Comme précédemment, on peut utiliser n'importe quelle structure HTML, par exemple:

<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>

ou

<my-paragraph>
  <ul slot="my-text">
    <li>Let's have some different text!</li>
    <li>In a list!</li>
  </ul>
</my-paragraph>

Note: Les éléments qui peuvent être insérés dans un slot sont dits {{domxref("Slotable")}}; quand un élément a été inséré dans un slot, il est dit slotted.

Et c'est tout pour ce premier exemple. Si vous souhaitez manipuler les slots, vous pouvez voir la page sur GitHub (voir aussi le résultat).

Un exemple plus complexe

Pour finir, voyons un exemple un peu moins trivial.

The following set of code snippets show how to use {{HTMLElement("slot")}} together with {{HTMLElement("template")}} and some JavaScript to:

Note that it is technically possible to use {{HTMLElement("slot")}} element without a {{HTMLElement("template")}} element, e.g., within say a regular {{HTMLElement("div")}} element, and still take advantage of the place-holder features of {{HTMLElement("slot")}} for Shadow DOM content, and doing so may indeed avoid the small trouble of needing to first access the template element's content property (and clone it). However, it is generally more practical to add slots within a {{HTMLElement("template")}} element, since you are unlikely to need to define a pattern based on an already-rendered element.

In addition, even if it is not already rendered, the purpose of the container as a template should be more semantically clear when using the {{HTMLElement("template")}}. In addition, {{HTMLElement("template")}} can have items directly added to it, like {{HTMLElement("td")}}, which would disappear when added to a {{HTMLElement("div")}}.

Note: You can find this complete example at element-details (see it running live also).

Creating a template with some slots

First of all, we use the {{HTMLElement("slot")}} element within a {{HTMLElement("template")}} element to create a new "element-details-template" document fragment containing some named slots:

<template id="element-details-template">
  <style>
  details {font-family: "Open Sans Light",Helvetica,Arial}
  .name {font-weight: bold; color: #217ac0; font-size: 120%}
  h4 { margin: 10px 0 -8px 0; }
  h4 span { background: #217ac0; padding: 2px 6px 2px 6px }
  h4 span { border: 1px solid #cee9f9; border-radius: 4px }
  h4 span { color: white }
  .attributes { margin-left: 22px; font-size: 90% }
  .attributes p { margin-left: 16px; font-style: italic }
  </style>
  <details>
    <summary>
      <span>
        <code class="name">&lt;<slot name="element-name">NEED NAME</slot>&gt;</code>
        <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
      </span>
    </summary>
    <div class="attributes">
      <h4><span>Attributes</span></h4>
      <slot name="attributes"><p>None</p></slot>
    </div>
  </details>
  <hr>
</template>

That {{HTMLElement("template")}} element has several features:

Creating a new <element-details> element from the <template>

Next, let’s create a new custom element named <element-details> and use {{DOMXref("Element.attachShadow")}} to attach to it, as its shadow root, that document fragment we created with our {{HTMLElement("template")}} element above. This uses exactly the same pattern as we saw in our earlier trivial example.

customElements.define('element-details',
  class extends HTMLElement {
    constructor() {
      super();
      var template = document
        .getElementById('element-details-template')
        .content;
      const shadowRoot = this.attachShadow({mode: 'open'})
        .appendChild(template.cloneNode(true));
  }
})

Using the <element-details> custom element with named slots

Now let’s take that <element-details> element and actually use it in our document:

<element-details>
  <span slot="element-name">slot</span>
  <span slot="description">A placeholder inside a web
    component that users can fill with their own markup,
    with the effect of composing different DOM trees
    together.</span>
  <dl slot="attributes">
    <dt>name</dt>
    <dd>The name of the slot.</dd>
  </dl>
</element-details>

<element-details>
  <span slot="element-name">template</span>
  <span slot="description">A mechanism for holding client-
    side content that is not to be rendered when a page is
    loaded but may subsequently be instantiated during
    runtime using JavaScript.</span>
</element-details>

About that snippet, notice these points:

Adding a final bit of style

As a finishing touch, we'll add a tiny bit more CSS for the {{HTMLElement("dl")}}, {{HTMLElement("dt")}}, and {{HTMLElement("dd")}} elements in our doc:

  dl { margin-left: 6px; }
  dt { font-weight: bold; color: #217ac0; font-size: 110% }
  dt { font-family: Consolas, "Liberation Mono", Courier }
  dd { margin-left: 16px }

Result

Finally let’s put all the snippets together and see what the rendered result looks like.

{{ EmbedLiveSample('full_example', '300','400','https://mdn.mozillademos.org/files/14553/element-details.png','') }}

Notice the following points about this rendered result: