diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:17 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:17 -0500 |
commit | da78a9e329e272dedb2400b79a3bdeebff387d47 (patch) | |
tree | e6ef8aa7c43556f55ddfe031a01cf0a8fa271bfe /files/it/web/web_components | |
parent | 1109132f09d75da9a28b649c7677bb6ce07c40c0 (diff) | |
download | translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.tar.gz translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.tar.bz2 translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.zip |
initial commit
Diffstat (limited to 'files/it/web/web_components')
-rw-r--r-- | files/it/web/web_components/index.html | 210 | ||||
-rw-r--r-- | files/it/web/web_components/usare_custom_elements/index.html | 297 |
2 files changed, 507 insertions, 0 deletions
diff --git a/files/it/web/web_components/index.html b/files/it/web/web_components/index.html new file mode 100644 index 0000000000..113e17af86 --- /dev/null +++ b/files/it/web/web_components/index.html @@ -0,0 +1,210 @@ +--- +title: Web Components +slug: Web/Web_Components +translation_of: Web/Web_Components +--- +<div>{{DefaultAPISidebar("Web Components")}}</div> + +<div class="summary"> +<p>Web Components è una suite di tecnologie che permettono di creare elementi personalizzati, la cui funzionalità è incapsulata e separata dal resto del codice sorgente, per uso in applicazioni web.</p> +</div> + +<h2 id="Concetti_e_uso">Concetti e uso</h2> + +<p>Riutilizzare una porzione di codice il più possibile è desiderabile. In passato, questo non è sempre stato facile per i linguaggi di markup: si pensi ad esempio al complesso HTML e CSS (con script associati) necessari per personalizzare i controlli dell'interfaccia utente, e a come sia necessario riutilizzarli all'interno della stessa pagina.</p> + +<p>I Web Component hanno l'obiettivo di risolvere questi problemi. Consistono di tre tecnologie principali che possono essere usate in combinazione per creare elementi personalizzati versatili con funzionalità incapsulata che possono essere riutilizzati senza dover temere collisioni nel codice.</p> + +<ul> + <li><strong>Elementi personalizzati</strong>: Un insieme di API per JavaScript che permettono di definire elementi personalizzati ed il loro comportamento.</li> + <li><strong>Shadow DOM</strong>: Un insieme di API per JavaScript per assegnare un albero DOM "nascosto" ad un elemento, e controllare la funzionalità associata. In questo modo, è possibile mantenere le caratteristiche dell'elemento private ed evitare collisioni con altre parti del documento.</li> + <li><strong>Template HTML</strong>: Gli elementi {{HTMLElement("template")}} e {{HTMLElement("slot")}} permettono di scrivere template di markup che non vengono visualizzati nella pagina. Questi template possono essere riutilizzati più volte come struttura base di un elemento personalizzato.</li> +</ul> + +<p>L'approccio all'implementazione di un web component è di solito la seguente:</p> + +<ol> + <li>Creare una classe o funzione in cui si specifica la funzionalità del componente. Se si usa una classe, è solito usare la sintassi ECMAScript 2015.</li> + <li>Registrare l'elemento tramite il metodo {{domxref("CustomElementRegistry.define()")}} passando come parametro il nome dell'elemento, la classe o funzione che specifica la funzionalità del componente, e (opzionalmente) l'elemento da cui eredita.</li> + <li>Se richiesto, assegnare un shadow DOM all'elemento tramite il metodo {{domxref("Element.attachShadow()")}} e aggiungere elementi figli, event listener eccetera allo shadow DOM tramite metodi standard del DOM.</li> + <li>Se richiesto, definire un template HTML usando {{htmlelement("template")}} e {{htmlelement("slot")}}. Usare i normali metodi del DOM per clonare il template e assegnarlo allo shadow DOM.</li> + <li>Usare l'elemento personalizzato dove necessario nella pagina, come un qualsiasi elemento HTML.</li> +</ol> + +<h2 id="Tutorial">Tutorial</h2> + +<dl> + <dt><a href="/en-US/docs/Web/Web_Components/Using_custom_elements">Usare elementi personalizzati</a></dt> + <dd>Una guida che mostra come usare elementi personalizzati per creare componenti web, istruzioni sui lifecycle callbacks, ed altre funzionalità avanzate.</dd> + <dt><a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">Usare lo shadow DOM</a></dt> + <dd>Una guida che spiega i fondamenti dello shadow DOM, mostrando come aggiungerlo a un elemento, aggiungere elementi all'albero shadow DOM, stilizzarlo, ed altro.</dd> + <dt><a href="/en-US/docs/Web/Web_Components/Using_templates_and_slots">Usare templates e slot</a></dt> + <dd>Una guida che spiega come definire una struttura HTML riutilizzabile usando gli elementi {{htmlelement("template")}} e {{htmlelement("slot")}}, e come usera la struttura in un web component.</dd> +</dl> + +<h2 id="Glossario">Glossario</h2> + +<h3 id="Elementi_personalizzati">Elementi personalizzati</h3> + +<dl> + <dt>{{domxref("CustomElementRegistry")}}</dt> + <dd>Contiene funzionalità relativa a elementi personalizzati, in particolare il metodo {{domxref("CustomElementRegistry.define()")}} usato per registrare nuovi elementi personalizzati in modo che possano essere usati nel documento.</dd> + <dt>{{domxref("Window.customElements")}}</dt> + <dd>Returns a reference to the <code>CustomElementRegistry</code> object.</dd> + <dt><a href="/en-US/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks">Life cycle callbacks</a></dt> + <dd>Special callback functions defined inside the custom element's class definition, which affect its behavior: + <ul> + <li><code>connectedCallback</code>: Invoked when the custom element is first connected to the document's DOM.</li> + <li><code>disconnectedCallback</code>: Invoked when the custom element is disconnected from the document's DOM.</li> + <li><code>adoptedCallback</code>: Invoked when the custom element is moved to a new document.</li> + <li><code>attributeChangedCallback</code>: Invoked when one of the custom element's attributes is added, removed, or changed.</li> + </ul> + </dd> + <dd> + <ul> + </ul> + </dd> +</dl> + +<dl> + <dt>Extensions for creating custom built-in elements</dt> + <dd> + <ul> + <li>The {{htmlattrxref("is")}} global HTML attribute: Allows you to specify that a standard HTML element should behave like a registered custom built-in element.</li> + <li>The "is" option of the {{domxref("Document.createElement()")}} method: Allows you to create an instance of a standard HTML element that behaves like a given registered custom built-in element.</li> + </ul> + </dd> + <dt>CSS pseudo-classes</dt> + <dd>Pseudo-classes relating specifically to custom elements: + <ul> + <li>{{cssxref(":defined")}}: Matches any element that is defined, including built in elements and custom elements defined with <code>CustomElementRegistry.define()</code>).</li> + <li>{{cssxref(":host")}}: Selects the shadow host of the <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> containing the CSS it is used inside.</li> + <li>{{cssxref(":host()")}}: Selects the shadow host of the <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> containing the CSS it is used inside (so you can select a custom element from inside its shadow DOM) — but only if the selector given as the function's parameter matches the shadow host.</li> + <li>{{cssxref(":host-context()")}}: Selects the shadow host of the <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> containing the CSS it is used inside (so you can select a custom element from inside its shadow DOM) — but only if the selector given as the function's parameter matches the shadow host's ancestor(s) in the place it sits inside the DOM hierarchy.</li> + </ul> + </dd> +</dl> + +<h3 id="Shadow_DOM">Shadow DOM</h3> + +<dl> + <dt>{{domxref("ShadowRoot")}}</dt> + <dd>Represents the root node of a shadow DOM subtree.</dd> + <dt>{{domxref("DocumentOrShadowRoot")}}</dt> + <dd>A mixin defining features that are available across document and shadow roots.</dd> + <dt>{{domxref("Element")}} extensions</dt> + <dd>Extensions to the <code>Element</code> interface related to shadow DOM: + <ul> + <li>The {{domxref("Element.attachShadow()")}} method attaches a shadow DOM tree to the specified element.</li> + <li>The {{domxref("Element.shadowRoot")}} property returns the shadow root attached to the specified element, or <code>null</code> if there is no shadow root attached.</li> + </ul> + </dd> + <dt>Relevant {{domxref("Node")}} additions</dt> + <dd>Additions to the <code>Node</code> interface relevant to shadow DOM: + <ul> + <li>The {{domxref("Node.getRootNode()")}} method returns the context object's root, which optionally includes the shadow root if it is available.</li> + <li>The {{domxref("Node.isConnected")}} property returns a boolean indicating whether or not the Node is connected (directly or indirectly) to the context object, e.g. the {{domxref("Document")}} object in the case of the normal DOM, or the {{domxref("ShadowRoot")}} in the case of a shadow DOM.</li> + </ul> + </dd> + <dt>{{domxref("Event")}} extensions</dt> + <dd>Extensions to the <code>Event</code> interface related to shadow DOM: + <ul> + <li>{{domxref("Event.composed")}}: Returns a {{jsxref("Boolean")}} which indicates whether the event will propagate across the shadow DOM boundary into the standard DOM (<code>true</code>), or not (<code>false</code>).</li> + <li>{{domxref("Event.composedPath")}}: Returns the event’s path (objects on which listeners will be invoked). This does not include nodes in shadow trees if the shadow root was created with {{domxref("ShadowRoot.mode")}} closed.</li> + </ul> + </dd> +</dl> + +<h3 id="HTML_templates">HTML templates</h3> + +<dl> + <dt>{{htmlelement("template")}}</dt> + <dd>Contains an HTML fragment that is not rendered when a containing document is initially loaded, but can be displayed at runtime using JavaScript, mainly used as the basis of custom element structures. The associated DOM interface is {{domxref("HTMLTemplateElement")}}.</dd> + <dt>{{htmlelement("slot")}}</dt> + <dd>A placeholder inside a web component that you can fill with your own markup, which lets you create separate DOM trees and present them together. The associated DOM interface is {{domxref("HTMLSlotElement")}}.</dd> + <dt>The <code><a href="/en-US/docs/Web/HTML/Global_attributes/slot">slot</a></code> global HTML attribute</dt> + <dd>Assigns a slot in a shadow DOM shadow tree to an element.</dd> + <dt>{{domxref("Slotable")}}</dt> + <dd>A mixin implemented by both {{domxref("Element")}} and {{domxref("Text")}} nodes, defining features that allow them to become the contents of an {{htmlelement("slot")}} element. The mixin defines one attribute, {{domxref("Slotable.assignedSlot")}}, which returns a reference to the slot the node is inserted in.</dd> +</dl> + +<dl> + <dt>{{domxref("Element")}} extensions</dt> + <dd>Extensions to the <code>Element</code> interface related to slots: + <ul> + <li>{{domxref("Element.slot")}}: Returns the name of the shadow DOM slot attached to the element.</li> + </ul> + </dd> + <dt>CSS pseudo-elements</dt> + <dd>Pseudo-elements relating specifically to slots: + <ul> + <li>{{cssxref("::slotted")}}: Matches any content that is inserted into a slot.</li> + </ul> + </dd> + <dt>The {{event("slotchange")}} event</dt> + <dd>Fired on an {{domxref("HTMLSlotElement")}} instance ({{htmlelement("slot")}} element) when the node(s) contained in that slot change.</dd> +</dl> + +<h2 id="Examples">Examples</h2> + +<p>We are building up a number of examples in our <a href="https://github.com/mdn/web-components-examples">web-components-examples</a> GitHub repo. More will be added as time goes on.</p> + +<h2 id="Specifications">Specifications</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName("HTML WHATWG","scripting.html#the-template-element","<template> element")}}</td> + <td>{{Spec2("HTML WHATWG")}}</td> + <td>The definition of {{HTMLElement("template")}}.</td> + </tr> + <tr> + <td>{{SpecName("HTML WHATWG","custom-elements.html#custom-elements","custom elements")}}</td> + <td>{{Spec2("HTML WHATWG")}}</td> + <td>The definition of <a href="/en-US/docs/Web/Web_Components/Using_custom_elements">HTML Custom Elements</a>.</td> + </tr> + <tr> + <td>{{SpecName("DOM WHATWG","#shadow-trees","shadow trees")}}</td> + <td>{{Spec2("DOM WHATWG")}}</td> + <td>The definition of <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a>.</td> + </tr> + <tr> + <td>{{SpecName("HTML Imports", "", "")}}</td> + <td>{{Spec2("HTML Imports")}}</td> + <td>Initial <a href="/en-US/docs/Web/Web_Components/HTML_Imports">HTML Imports</a> definition.</td> + </tr> + <tr> + <td>{{SpecName("Shadow DOM", "", "")}}</td> + <td>{{Spec2("Shadow DOM")}}</td> + <td>Initial <a href="/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a> definition.</td> + </tr> + </tbody> +</table> + +<h2 id="Browser_compatibility">Browser compatibility</h2> + +<p>In general:</p> + +<ul> + <li>Web components are supported by default in Firefox (version 63), Chrome, and Opera.</li> + <li>Safari supports a number of web component features, but less than the above browsers.</li> + <li>Edge is working on an implementation.</li> +</ul> + +<p>For detailed browser support of specific features, you'll have to consult the reference pages listed above.</p> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="https://www.webcomponents.org/">webcomponents.org</a> — site featuring web components examples, tutorials, and other information.</li> + <li><a href="https://github.com/hybridsjs/hybrids">Hybrids</a> — Open source web components library, which favors plain objects and pure functions over <code>class</code> and <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">this</span></font> syntax. It provides a simple and functional API for creating custom elements.</li> + <li><a href="https://www.polymer-project.org/">Polymer</a> — Google's web components framework — a set of polyfills, enhancements, and examples. Currently the easiest way to use web components cross-browser.</li> + <li><a href="https://github.com/devpunks/snuggsi#readme">Snuggsi</a> — Easy Web Components in ~1kB <em>Including polyfill</em> — All you need is a browser and basic understanding of HTML, CSS, and JavaScript classes to be productive.</li> + <li><a href="https://github.com/slimjs/slim.js">Slim.js</a> — Open source web components library — a high-performant library for rapid and easy component authoring; extensible and pluggable and cross-framework compatible.</li> + <li><a href="https://stenciljs.com/">Stencil</a> — Toolchain for building reusable, scalable design systems in web components.</li> +</ul> diff --git a/files/it/web/web_components/usare_custom_elements/index.html b/files/it/web/web_components/usare_custom_elements/index.html new file mode 100644 index 0000000000..4fa75cb380 --- /dev/null +++ b/files/it/web/web_components/usare_custom_elements/index.html @@ -0,0 +1,297 @@ +--- +title: Usare i custom elements +slug: Web/Web_Components/Usare_custom_elements +translation_of: Web/Web_Components/Using_custom_elements +--- +<div>{{DefaultAPISidebar("Web Components")}}</div> + +<p class="summary">Una delle caratteristiche chiave dei Web Components standard è la capacità di creare elementi personalizzati che contengono le funzionalità che vuoi sviluppare direttamente in una pagina HTML, anzichè che sviluppare una lunga serie di singoli elementi innestati per avere, in una pagina, le funzionalità che desideri. Questo articolo ti introduce all'uso delle Custom Elements API.</p> + +<div class="note"> +<p><strong>Nota</strong>: I custom elements sono supportati di default in Firefox, Chrome, e Edge (76). Opera e Safari fino ad ora supportano solo custom elements proprietari.</p> +</div> + +<h2 id="Panoramica_generale">Panoramica generale</h2> + +<p>Il controller dei custom elements in un web document è l'oggetto {{domxref("CustomElementRegistry")}} — questo oggetto ti permette di registrareun custom element nella pagina, ritornare informazioni su cosa restituire informazioni su quali elementi personalizzati sono registrati etc.</p> + +<p>Per registrare un custom element nella pagina, usa il metodo {{domxref("CustomElementRegistry.define()")}}. Questo metodo ha questi argomenti:</p> + +<ul> + <li>Il {{domxref("DOMString")}} rappresenta il nome che vuoi dare all'elemento. Ricorda che i nomi dei custom elements <a href="https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name">richiedono un trattino</a> (kebab-case); non possono essere una singole parole.</li> + <li>Un oggetto <a href="/en-US/docs/Web/JavaScript/Reference/Classes">classe</a> che definisce le funzionalità dell'elemento.</li> + <li>Opzionalmente, un oggetto contenente una proprietà <code>extends</code>, che specifica le caratteristiche dell'elemento che vengono ereditate nel custom element creato.</li> +</ul> + +<p>Per esempio, possiamo definire un custom <a href="https://mdn.github.io/web-components-examples/word-count-web-component/">word-count element</a> come questo:</p> + +<pre class="brush: js">customElements.define('word-count', WordCount, { extends: 'p' });</pre> + +<p>L'elemento è chiamato <code>word-count</code>, la sua classe è <code>WordCount</code>, ed estende l'elemento {{htmlelement("p")}}.</p> + +<p>Una classe custom element viene scritta usando la sintassi standard ES 2015. Per esempio, <code>WordCount</code> è strutturata così:</p> + +<pre class="brush: js">class WordCount extends HTMLParagraphElement { + constructor() { + // Always call super first in constructor + super(); + + // Element functionality written in here + + ... + } +}</pre> + +<p>This is just a simple example, but there is more you can do here. It is possible to define specific lifecycle callbacks inside the class, which run at specific points in the element's lifecycle. For example, <code>connectedCallback</code> is invoked each time the custom element is appended into a document-connected element, while <code>attributeChangedCallback</code> is invoked when one of the custom element's attributes is added, removed, or changed.</p> + +<p>You'll learn more about these in the {{anch("Using the lifecycle callbacks")}} section below.</p> + +<p>There are two types of custom elements:</p> + +<ul> + <li><strong>Autonomous custom elements</strong> are standalone — they don't inherit from standard HTML elements. You use these on a page by literally writing them out as an HTML element. For example <code><popup-info></code>, or <code>document.createElement("popup-info")</code>.</li> + <li><strong>Customized built-in elements</strong> inherit from basic HTML elements. To create one of these, you have to specify which element they extend (as implied in the examples above), and they are used by writing out the basic element but specifying the name of the custom element in the {{htmlattrxref("is")}} attribute (or property). For example <code><p is="word-count"></code>, or <code>document.createElement("p", { is: "word-count" })</code>.</li> +</ul> + +<h2 id="Working_through_some_simple_examples">Working through some simple examples</h2> + +<p>At this point, let's go through some more simple examples to show you how custom elements are created in more detail.</p> + +<h3 id="Autonomous_custom_elements">Autonomous custom elements</h3> + +<p>Let's have a look at an example of an autonomous custom element — <code><a href="https://github.com/mdn/web-components-examples/tree/master/popup-info-box-web-component"><popup-info-box></a></code> (see a <a href="https://mdn.github.io/web-components-examples/popup-info-box-web-component/">live example</a>). This takes an image icon and a text string, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information.</p> + +<p>To begin with, the JavaScript file defines a class called <code>PopUpInfo</code>, which extends {{domxref("HTMLElement")}}. Autonomous custom elements nearly always extend <code>HTMLElement</code>.</p> + +<pre class="brush: js">class PopUpInfo extends HTMLElement { + constructor() { + // Always call super first in constructor + super(); + + // write element functionality in here + + ... + } +}</pre> + +<p>The preceding code snippet contains the <code><a href="/en-US/docs/Web/JavaScript/Reference/Classes/constructor">constructor()</a></code> definition for the class, which always starts by calling <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/super">super()</a></code> so that the correct prototype chain is established.</p> + +<p>Inside the constructor, we define all the functionality the element will have when an instance of it is instantiated. In this case we attach a shadow root to the custom element, use some DOM manipulation to create the element's internal shadow DOM structure — which is then attached to the shadow root — and finally attach some CSS to the shadow root to style it.</p> + +<pre class="brush: js">// Create a shadow root +var shadow = this.attachShadow({mode: 'open'}); + +// Create spans +var wrapper = document.createElement('span'); +wrapper.setAttribute('class','wrapper'); +var icon = document.createElement('span'); +icon.setAttribute('class','icon'); +icon.setAttribute('tabindex', 0); +var info = document.createElement('span'); +info.setAttribute('class','info'); + +// Take attribute content and put it inside the info span +var text = this.getAttribute('text'); +info.textContent = text; + +// Insert icon +var imgUrl; +if(this.hasAttribute('img')) { + imgUrl = this.getAttribute('img'); +} else { + imgUrl = 'img/default.png'; +} +var img = document.createElement('img'); +img.src = imgUrl; +icon.appendChild(img); + +// Create some CSS to apply to the shadow dom +var style = document.createElement('style'); + +style.textContent = '.wrapper {' + +// CSS truncated for brevity + +// attach the created elements to the shadow dom + +shadow.appendChild(style); +shadow.appendChild(wrapper); +wrapper.appendChild(icon); +wrapper.appendChild(info);</pre> + +<p>Finally, we register our custom element on the <code>CustomElementRegistry</code> using the <code>define()</code> method we mentioned earlier — in the parameters we specify the element name, and then the class name that defines its functionality:</p> + +<pre class="brush: js">customElements.define('popup-info', PopUpInfo);</pre> + +<p>It is now available to use on our page. Over in our HTML, we use it like so:</p> + +<pre class="brush: html"><popup-info img="img/alt.png" text="Your card validation code (CVC) + is an extra security feature — it is the last 3 or 4 numbers on the + back of your card."></popup-info></pre> + +<div class="note"> +<p><strong>Note</strong>: You can see the <a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-web-component/main.js">full JavaScript source</a> code here.</p> +</div> + +<div class="blockIndicator note"> +<p><strong>Note</strong>: Remember that for the custom element to work, the script that registers it has to be loaded after the DOM is parsed. This can be done either by including the <code><script></code> element at the bottom of the <code><body></code>, or by including the <code>defer</code> attribute in your <code><script></code> element.</p> +</div> + +<h3 id="Internal_vs._external_styles">Internal vs. external styles</h3> + +<p>In the above example we apply style to the Shadow DOM using a {{htmlelement("style")}} element, but it is perfectly possible to do it by referencing an external stylesheet from a {{htmlelement("link")}} element instead.</p> + +<p>For example, take a look at this code from our <a href="https://mdn.github.io/web-components-examples/popup-info-box-external-stylesheet/">popup-info-box-external-stylesheet</a> example (see the <a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-external-stylesheet/main.js">source code</a>):</p> + +<pre class="brush: js">// Apply external styles to the shadow dom +const linkElem = document.createElement('link'); +linkElem.setAttribute('rel', 'stylesheet'); +linkElem.setAttribute('href', 'style.css'); + +// Attach the created element to the shadow dom +shadow.appendChild(linkElem);</pre> + +<p>Note that {{htmlelement("link")}} elements do not block paint of the shadow root, so there may be a flash of unstyled content (FOUC) while the stylesheet loads.</p> + +<p>Many modern browsers implement an optimization for {{htmlelement("style")}} tags either cloned from a common node or that have identical text, to allow them to share a single backing stylesheet. With this optimization the performance of external and internal styles should be similar.</p> + +<h3 id="Customized_built-in_elements">Customized built-in elements</h3> + +<p>Now let's have a look at another customized built in element example — <a href="https://github.com/mdn/web-components-examples/tree/master/expanding-list-web-component">expanding-list</a> (<a href="https://mdn.github.io/web-components-examples/expanding-list-web-component/">see it live also</a>). This turns any unordered list into an expanding/collapsing menu.</p> + +<p>First of all, we define our element's class, in the same manner as before:</p> + +<pre class="brush: js">class ExpandingList extends HTMLUListElement { + constructor() { + // Always call super first in constructor + super(); + + // write element functionality in here + + ... + } +}</pre> + +<p>We will not explain the element functionality in any detail here, but you can discover how it works by checking out the source code. The only real difference here is that our element is extending the {{domxref("HTMLUListElement")}} interface, and not {{domxref("HTMLElement")}}. So it has all the characteristics of a {{htmlelement("ul")}} element with the functionality we define built on top, rather than being a standalone element. This is what makes it a customized built-in, rather than an autonomous element.</p> + +<p>Next, we register the element using the <code>define()</code> method as before, except that this time it also includes an options object that details what element our custom element inherits from:</p> + +<pre class="brush: js">customElements.define('expanding-list', ExpandingList, { extends: "ul" });</pre> + +<p>Using the built-in element in a web document also looks somewhat different:</p> + +<pre class="brush: html"><ul is="expanding-list"> + + ... + +</ul></pre> + +<p>You use a <code><ul></code> element as normal, but specify the name of the custom element inside the <code>is</code> attribute.</p> + +<div class="note"> +<p><strong>Note</strong>: Again, you can see the full <a href="https://github.com/mdn/web-components-examples/blob/master/expanding-list-web-component/main.js">JavaScript source code</a> here.</p> +</div> + +<h2 id="Using_the_lifecycle_callbacks">Using the lifecycle callbacks</h2> + +<p>You can define several different callbacks inside a custom element's class definition, which fire at different points in the element's lifecycle:</p> + +<ul> + <li><code>connectedCallback</code>: Invoked each time the custom element is appended into a document-connected element. This will happen each time the node is moved, and may happen before the element's contents have been fully parsed. + + <div class="note"> + <p><strong>Note</strong>: <code>connectedCallback</code> may be called once your element is no longer connected, use {{domxref("Node.isConnected")}} to make sure.</p> + </div> + </li> + <li><code>disconnectedCallback</code>: Invoked each time the custom element is disconnected from the document's DOM.</li> + <li><code>adoptedCallback</code>: Invoked each time the custom element is moved to a new document.</li> + <li><code>attributeChangedCallback</code>: Invoked each time one of the custom element's attributes is added, removed, or changed. Which attributes to notice change for is specified in a static get <code>observedAttributes</code> method</li> +</ul> + +<p>Let's look at an example of these in use. The code below is taken from the <a href="https://github.com/mdn/web-components-examples/tree/master/life-cycle-callbacks">life-cycle-callbacks</a> example (<a href="https://mdn.github.io/web-components-examples/life-cycle-callbacks/">see it running live</a>). This is a trivial example that simply generates a colored square of a fixed size on the page. The custom element looks like this:</p> + +<pre class="brush: html"><custom-square l="100" c="red"></custom-square></pre> + +<p>The class constructor is really simple — here we attach a shadow DOM to the element, then attach empty {{htmlelement("div")}} and {{htmlelement("style")}} elements to the shadow root:</p> + +<pre class="brush: js">var shadow = this.attachShadow({mode: 'open'}); + +var div = document.createElement('div'); +var style = document.createElement('style'); +shadow.appendChild(style); +shadow.appendChild(div);</pre> + +<p>The key function in this example is <code>updateStyle()</code> — this takes an element, gets its shadow root, finds its <code><style></code> element, and adds {{cssxref("width")}}, {{cssxref("height")}}, and {{cssxref("background-color")}} to the style.</p> + +<pre class="brush: js">function updateStyle(elem) { + const shadow = elem.shadowRoot; + shadow.querySelector('style').textContent = ` + div { + width: ${elem.getAttribute('l')}px; + height: ${elem.getAttribute('l')}px; + background-color: ${elem.getAttribute('c')}; + } + `; +}</pre> + +<p>The actual updates are all handled by the life cycle callbacks, which are placed inside the class definition as methods. The <code>connectedCallback()</code> runs each time the element is added to the DOM — here we run the <code>updateStyle()</code> function to make sure the square is styled as defined in its attributes:</p> + +<pre class="brush: js">connectedCallback() { + console.log('Custom square element added to page.'); + updateStyle(this); +}</pre> + +<p>The <code>disconnectedCallback()</code> and <code>adoptedCallback()</code> callbacks log simple messages to the console to inform us when the element is either removed from the DOM, or moved to a different page:</p> + +<pre class="brush: js">disconnectedCallback() { + console.log('Custom square element removed from page.'); +} + +adoptedCallback() { + console.log('Custom square element moved to new page.'); +}</pre> + +<p>The <code>attributeChangedCallback()</code> callback is run whenever one of the element's attributes is changed in some way. As you can see from its properties, it is possible to act on attributes individually, looking at their name, and old and new attribute values. In this case however, we are just running the <code>updateStyle()</code> function again to make sure that the square's style is updated as per the new values:</p> + +<pre class="brush: js">attributeChangedCallback(name, oldValue, newValue) { + console.log('Custom square element attributes changed.'); + updateStyle(this); +}</pre> + +<p>Note that to get the <code>attributeChangedCallback()</code> callback to fire when an attribute changes, you have to observe the attributes. This is done by specifying a <code>static get observedAttributes()</code> method inside custom element class - this should <code>return</code> an array containing the names of the attributes you want to observe:</p> + +<pre class="brush: js">static get observedAttributes() { return ['c', 'l']; }</pre> + +<p>This is placed right at the top of the constructor, in our example.</p> + +<div class="note"> +<p><strong>Note</strong>: Find the <a href="https://github.com/mdn/web-components-examples/blob/master/life-cycle-callbacks/main.js">full JavaScript source</a> here.</p> +</div> + +<h2 id="Polyfills_vs._classes">Polyfills vs. classes</h2> + +<p>Custom Element polyfills may patch native constructors such as <code>HTMLElement</code> and others, and return a different instance from the one just created.</p> + +<p>If you need a <code>constructor</code> and a mandatory <code>super</code> call, remember to pass along optional arguments and return the result of such a <code>super</code> call operation.</p> + +<pre class="brush: js">class CustomElement extends HTMLElement { + constructor(...args) { + const self = super(...args); + // self functionality written in here + // self.addEventListener(...) + // return the right context + return self; + } +}</pre> + +<p>If you don't need to perform any operation in the constructor, you can simply omit it so that its native behavior (see following) will be preserved.</p> + +<pre class="brush: js"> constructor(...args) { return super(...args); } +</pre> + +<h2 id="Transpilers_vs._classes">Transpilers vs. classes</h2> + +<p>Please note that ES2015 classes cannot reliably be transpiled in Babel 6 or TypeScript targeting legacy browsers. You can either use Babel 7 or the <a href="https://www.npmjs.com/package/babel-plugin-transform-builtin-classes">babel-plugin-transform-builtin-classes</a> for Babel 6, and target ES2015 in TypeScript instead of legacy.</p> + +<h2 id="Libraries">Libraries</h2> + +<p>There are several libraries that are built on Web Components with the aim of increasing the level of abstraction when creating custom elements. Some of these libraries are <a href="https://github.com/devpunks/snuggsi" rel="nofollow">snuggsi ツ</a>, <a href="https://x-tag.github.io/" rel="nofollow">X-Tag</a>, <a href="http://slimjs.com/" rel="nofollow">Slim.js</a>, <a href="https://lit-element.polymer-project.org/">LitElement</a>, <a href="https://www.htmlelements.com/">Smart</a>, and <a href="https://stenciljs.com">Stencil</a>.</p> |