1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
|
---
title: Utilisation des templates et des slots
slug: Web/Web_Components/Utilisation_des_templates_et_des_slots
tags:
- Composant web
- HTML
- Template
- shadow dom
- slot
translation_of: Web/Web_Components/Using_templates_and_slots
---
<div>{{DefaultAPISidebar("Web Components")}}</div>
<p class="summary">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.</p>
<h2 id="La_vérité_sur_les_templates">La vérité sur les templates</h2>
<p>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.</p>
<p>Voyons un exemple simple:</p>
<pre class="brush: html"><template id="my-paragraph">
<p>My paragraph</p>
</template></pre>
<p>Le tag <code><template></code> 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:</p>
<pre class="brush: js">let template = document.getElementById('my-paragraph');
let templateContent = template.content;
document.body.appendChild(templateContent);</pre>
<p>Quoique trivial, cet exemple vous permet d'entrevoir l'interêt d'utiliser des templates.</p>
<h2 id="Accorder_templates_et_composants_Web">Accorder templates et composants Web</h2>
<p>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 <code><my-paragraph></code>:</p>
<pre class="brush: js">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));
}
})</pre>
<p>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()")}}.</p>
<p>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.</p>
<p>Par exemple:</p>
<pre class="brush: html"><template id="my-paragraph">
<style>
p {
color: white;
background-color: #666;
padding: 5px;
}
</style>
<p>My paragraph</p>
</template></pre>
<p>On peut maintenent utiliser le template dans le document HTML:</p>
<pre class="brush: html"><my-paragraph></my-paragraph>
</pre>
<div class="note">
<p><strong>Note</strong>: 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.</p>
</div>
<h2 id="Plus_de_flexibilité_avec_les_slots">Plus de flexibilité avec les slots</h2>
<p>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.</p>
<p>Les slots sont identifiés par leur attribut <code>name</code>, et permettent de définir des champs dans le template qui peuvent être alimentés avec n'importe quelle structure HTML.</p>
<p>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>
<pre class="brush: html"><p><slot name="my-text">My default text</slot></p></pre>
<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, <code><my-paragraph></code> contient simplement le texte statique précisé dans le template.</p>
<p>Pour définir le contenu du slot, on insère une structure HTML dans <code><my-paragraph></code> 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:</p>
<pre class="brush: html"><my-paragraph>
<span slot="my-text">Let's have some different text!</span>
</my-paragraph></pre>
<p>ou</p>
<pre class="brush: html"><my-paragraph>
<ul slot="my-text">
<li>Let's have some different text!</li>
<li>In a list!</li>
</ul>
</my-paragraph>
</pre>
<div class="note">
<p><strong>Note</strong>: 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 <em>slotted</em>.</p>
</div>
<p>Et c'est tout pour ce premier exemple. Si vous souhaitez manipuler les slots, vous pouvez <a href="https://github.com/mdn/web-components-examples/tree/master/simple-template">voir la page sur GitHub</a> (voir aussi <a href="https://mdn.github.io/web-components-examples/simple-template/">le résultat</a>).</p>
<h2 id="Un_exemple_plus_complexe">Un exemple plus complexe</h2>
<p>Pour finir, voyons un exemple un peu moins trivial.</p>
<p>The following set of code snippets show how to use {{HTMLElement("slot")}} together with {{HTMLElement("template")}} and some JavaScript to:</p>
<ul>
<li>create a <strong><code><element-details></code></strong> element with <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a> in its <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a></li>
<li>design the <strong><code><element-details></code></strong> element in such a way that, when used in documents, it is rendered from composing the element’s content together with content from its <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>—that is, pieces of the element’s content are used to fill in <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a> in its <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a></li>
</ul>
<p>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 <code>content</code> 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.</p>
<p>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")}}.</p>
<div class="note">
<p><strong>Note</strong>: You can find this complete example at <a href="https://github.com/mdn/web-components-examples/tree/master/element-details">element-details</a> (see it <a href="https://mdn.github.io/web-components-examples/element-details/">running live</a> also).</p>
</div>
<h3 id="Creating_a_template_with_some_slots">Creating a template with some slots</h3>
<p>First of all, we use the {{HTMLElement("slot")}} element within a {{HTMLElement("template")}} element to create a new "element-details-template" <a href="/en-US/docs/Web/API/DocumentFragment">document fragment</a> containing some <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a>:</p>
<pre class="brush: html"><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>
</pre>
<p>That {{HTMLElement("template")}} element has several features:</p>
<ul>
<li>The {{HTMLElement("template")}} has a {{HTMLElement("style")}} element with a set of CSS styles that are scoped just to the document fragment the {{HTMLElement("template")}} creates.</li>
<li>The {{HTMLElement("template")}} uses {{HTMLElement("slot")}} and its {{htmlattrxref("name", "slot")}} attribute to make three <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a>:
<ul>
<li><code><slot name="element-name"></code></li>
<li><code><slot name="description"></code></li>
<li><code><slot name="attributes"></code></li>
</ul>
</li>
<li>The {{HTMLElement("template")}} wraps the <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a> in a {{HTMLElement("details")}} element.</li>
</ul>
<h3 id="Creating_a_new_<element-details>_element_from_the_<template>">Creating a new <element-details> element from the <template></h3>
<p>Next, let’s create a new custom element named <strong><code><element-details></code></strong> and use {{DOMXref("Element.attachShadow")}} to attach to it, as its <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>, 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.</p>
<pre class="brush: js">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));
}
})
</pre>
<h3 id="Using_the_<element-details>_custom_element_with_named_slots">Using the <element-details> custom element with named slots</h3>
<p>Now let’s take that <strong><code><element-details></code></strong> element and actually use it in our document:</p>
<pre class="brush: html"><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>
</pre>
<p>About that snippet, notice these points:</p>
<ul>
<li>The snippet has two instances of <strong><code><element-details></code></strong> elements which both use the {{htmlattrxref("slot")}} attribute to reference the <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a> <code>"element-name"</code> and <code>"description"</code> we put in the <code><element-details></code> <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a> .</li>
<li>Only the first of those two <strong><code><element-details></code></strong> elements references the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a>. The second <code><strong><element-details</strong>></code> element lacks any reference to the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a>.</li>
<li>The first <code><<strong>element-details></strong></code> element references the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a> using a {{HTMLElement("dl")}} element with {{HTMLElement("dt")}} and {{HTMLElement("dd")}} children.</li>
</ul>
<h3 id="Adding_a_final_bit_of_style">Adding a final bit of style</h3>
<p>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:</p>
<pre class="brush: css"> dl { margin-left: 6px; }
dt { font-weight: bold; color: #217ac0; font-size: 110% }
dt { font-family: Consolas, "Liberation Mono", Courier }
dd { margin-left: 16px }
</pre>
<div class="hidden">
<pre class="brush: css">body { margin-top: 47px }</pre>
</div>
<h3 id="Result">Result</h3>
<p>Finally let’s put all the snippets together and see what the rendered result looks like.</p>
<p>{{ EmbedLiveSample('full_example', '300','400','https://mdn.mozillademos.org/files/14553/element-details.png','') }}</p>
<p>Notice the following points about this rendered result:</p>
<ul>
<li>Even though the instances of the <strong><code><element-details></code></strong> element in the document do not directly use the {{HTMLElement("details")}} element, they get rendered using {{HTMLElement("details")}} because the <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a> causes them to get populated with that.</li>
<li>Within the rendered {{HTMLElement("details")}} output, the content in the <strong><code><element-details></code></strong> elements fills the <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slots</a> from the <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>. In other words, the DOM tree from the <strong><code><element-details></code></strong> elements get <em>composed</em> together with the content of the <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>.</li>
<li>For both <strong><code><element-details></code></strong> elements, an <strong>Attributes</strong> heading gets automatically added from the <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a> before the position of the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a>.</li>
<li>Because the first <strong><code><element-details></code></strong> has a {{HTMLElement("dl")}} element which explicitly references the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a> from its <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>, the contents of that {{HTMLElement("dl")}} replace the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a> from the <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>.</li>
<li>Because the second <strong><code><element-details></code></strong> doesn’t explicitly reference the <code>"attributes"</code> <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a> from its <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>, its content for that <a href="/en-US/docs/Web/HTML/Element/slot#named-slot">named slot</a> gets filled with the default content for it from the <a href="/en-US/docs/Web/API/ShadowRoot">shadow root</a>.</li>
</ul>
<div class="hidden">
<h5 id="full_example">full example</h5>
<pre class="brush: html"><!DOCTYPE html>
<html>
<head>
<title>slot example</title>
<style>
dl { margin-left: 6px; }
dt { font-weight: bold; color: #217ac0; font-size: 110% }
dt { font-family: Consolas, "Liberation Mono", Courier }
dd { margin-left: 16px }
</style>
</head>
<body>
<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>
<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>
<script>
customElements.define('element-details',
class extends HTMLElement {
constructor() {
super();
const template = document
.getElementById('element-details-template')
.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(template.cloneNode(true));
}
})
</script>
</body>
</html></pre>
</div>
|