Custom Elements v1 – Composants Web réutilisables

Les éléments personnalisés permettent aux développeurs Web de définir de nouvelles balises HTML, d'étendre les balises existantes et de créer des composants Web réutilisables.

Avec les éléments personnalisés, les développeurs Web peuvent créer des balises HTML, Complétez les balises HTML existantes ou élargissez les composants dont disposent les autres développeurs. n'est pas rédigée. L'API est à la base du Web composants. Il présente une interface Web standardisé de créer des composants réutilisables en n'utilisant que JS/HTML/CSS vanilla. Résultat : moins de code, de code modulaire et plus de réutilisation nos applications.

Introduction

Le navigateur constitue un excellent outil pour structurer les applications Web. Il est appelée HTML. Vous en avez peut-être entendu parler ! Il est déclaratif, portable, pris en charge et faciles à utiliser. Le langage HTML est très utile, car son vocabulaire et leur extensibilité est limitée. L'expérience HTML vivante standard n'a toujours pas pu associer automatiquement le comportement JS à votre balisage, jusqu'à maintenant.

Les éléments personnalisés sont la solution à la modernisation du code HTML, en comblant les et le regroupement de la structure avec le comportement. Si le code HTML ne fournit pas la solution à un problème, nous pouvons créer un élément personnalisé qui le fait. Personnalisé enseignent de nouvelles astuces au navigateur tout en conservant les avantages du langage HTML.

Définir un nouvel élément

Pour définir un nouvel élément HTML, nous avons besoin de la puissance de JavaScript !

Le global customElements permet de définir un élément personnalisé et d'enseigner le navigateur à propos d'un nouveau tag. Appelez customElements.define() avec le nom de la balise. que vous souhaitez créer et un class JavaScript qui étend la HTMLElement de base.

Exemple – Définition d'un panneau de panneau pour mobile, <app-drawer>:

class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);

// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});

Exemple d'utilisation :

<app-drawer></app-drawer>

N'oubliez pas que l'utilisation d'un élément personnalisé n'est pas différente à l'aide d'un <div> ou de tout autre élément. Vous pouvez déclarer des instances sur la page, créés dynamiquement en JavaScript, vous pouvez associer des écouteurs d'événements, etc. pour découvrir d'autres exemples.

Définir l'API JavaScript d'un élément

La fonctionnalité d'un élément personnalisé est définie à l'aide d'un class qui étend HTMLElement. L'extension de HTMLElement garantit que l'élément personnalisé hérite de l'ensemble de l'API DOM, ce qui signifie que toutes les propriétés/méthodes que vous ajoutez font partie intégrante de l'interface DOM de l'élément. En gros, utilisez la classe pour créez une API JavaScript publique pour votre tag.

Exemple – Définir l'interface DOM de <app-drawer>:

class AppDrawer extends HTMLElement {

  // A getter/setter for an open property.
  get open() {
    return this.hasAttribute('open');
  }

  set open(val) {
    // Reflect the value of the open property as an HTML attribute.
    if (val) {
      this.setAttribute('open', '');
    } else {
      this.removeAttribute('open');
    }
    this.toggleDrawer();
  }

  // A getter/setter for a disabled property.
  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    // Reflect the value of the disabled property as an HTML attribute.
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Can define constructor arguments if you wish.
  constructor() {
    // If you define a constructor, always call super() first!
    // This is specific to CE and required by the spec.
    super();

    // Setup a click listener on <app-drawer> itself.
    this.addEventListener('click', e => {
      // Don't toggle the drawer if it's disabled.
      if (this.disabled) {
        return;
      }
      this.toggleDrawer();
    });
  }

  toggleDrawer() {
    // ...
  }
}

customElements.define('app-drawer', AppDrawer);

Dans cet exemple, nous créons un panneau doté de la propriété open, disabled. et une méthode toggleDrawer(). De plus, il reflète les propriétés au format HTML Attributs.

Une caractéristique intéressante des éléments personnalisés est que this dans une définition de classe fait référence à l'élément DOM lui-même, c'est-à-dire à l'instance de la classe. Dans notre exemple, this fait référence à <app-drawer>. Voilà (😉) Voilà comment l'élément peut associer un écouteur click à lui-même ! Vous n'êtes pas limité aux écouteurs d'événements. L'ensemble de l'API DOM est disponible dans le code de l'élément. Utilisez this pour accéder à les propriétés de l'élément, inspecter ses enfants (this.children), les nœuds de requête (this.querySelectorAll('.items')), etc.

Règles relatives à la création d'éléments personnalisés

  1. Le nom d'un élément personnalisé doit contenir un tiret (-). Donc <x-tags>, <my-element> et <my-awesome-app> sont tous des noms valides, tandis que <tabs> et <foo_bar> ne le sont pas. Cette exigence permet à l'analyseur HTML de distinguer les éléments personnalisés des éléments standards ; Cela garantit également l’avancement lors de l'ajout de nouvelles balises au code HTML.
  2. Vous ne pouvez pas enregistrer la même balise plusieurs fois. Si vous essayez de le faire, générer une DOMException. Une fois que vous avez signalé un nouveau tag au navigateur, Aucun détournement possible.
  3. Les éléments personnalisés ne peuvent pas se fermer automatiquement, car le code HTML n'autorise que quelques éléments éléments se fermer automatiquement. Toujours écrire une balise de fermeture (<app-drawer></app-drawer>).

Réactions d'éléments personnalisés

Un élément personnalisé peut définir des hooks de cycle de vie spéciaux pour exécuter du code pendant des moments intéressants de son existence. Il s'agit d'éléments personnalisés de réaction.

Nom Appelé lorsque
constructor Une instance de l'élément est créées ou mises à niveau. Utile pour l'initialisation en configurant des écouteurs d'événements en créant une "shadow dom". Consultez le caractéristiques techniques pour connaître les restrictions sur les actions possibles dans constructor.
connectedCallback Appelé à chaque fois que la méthode est inséré dans le DOM. Utile pour exécuter du code de configuration, par exemple l'extraction de ressources ou le rendu. En général, vous devez essayer de retarder le travail jusqu'à cette date.
disconnectedCallback Appelé chaque fois que l'élément est supprimé du DOM. Avantages et l'exécution du code de nettoyage.
attributeChangedCallback(attrName, oldVal, newVal) Appelé lorsqu'un attribut observé a été ajoutées, supprimées, mises à jour ou remplacées. Également appelé pour les valeurs initiales lorsqu'un élément est créé par l'analyseur ; mis à niveau. Remarque:uniquement les attributs listés dans la propriété observedAttributes recevoir ce rappel.
adoptedCallback La élément personnalisé a été déplacé dans un nouveau document (par exemple, quelqu'un appelé document.adoptNode(el)).

Les rappels de réaction sont synchrones. Si quelqu'un appelle le el.setAttribute() sur votre élément, le navigateur appelle immédiatement attributeChangedCallback(). De même, vous recevrez une disconnectedCallback() juste après que votre élément soit supprimé du DOM (par exemple, l'utilisateur appelle el.remove()).

Exemple:Ajouter des réactions d'éléments personnalisés à <app-drawer>:

class AppDrawer extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.
    // ...
  }

  connectedCallback() {
    // ...
  }

  disconnectedCallback() {
    // ...
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    // ...
  }
}

Définissez les réactions si elles ont du sens ou quand elles le sont. Si votre élément est suffisamment complexe et ouvre une connexion à IndexedDB dans connectedCallback(), effectuez les opérations nécessaires de nettoyage dans disconnectedCallback(). Mais faites attention ! Vous ne pouvez pas compter sur d'être supprimé du DOM en toutes circonstances. Par exemple, disconnectedCallback() ne sera jamais appelé si l'utilisateur ferme l'onglet.

Propriétés et attributs

Faire correspondre les propriétés aux attributs

Il n'est pas rare que les propriétés HTML reflètent leur valeur dans le DOM en tant que HTML. Par exemple, lorsque les valeurs de hidden ou id sont modifiées dans JS:

div.id = 'my-id';
div.hidden = true;

les valeurs sont appliquées au DOM actif en tant qu'attributs:

<div id="my-id" hidden>

C'est ce qu'on appelle "les propriétés "Attributs"". Presque toutes les propriétés en HTML font cela. Pourquoi ? Les attributs sont également utiles pour la configuration d'un élément de manière déclarative et de certaines API, telles que l'accessibilité et les CSS, les sélecteurs s'appuient sur des attributs pour fonctionner.

Le fait de refléter une propriété est utile partout où vous souhaitez conserver le DOM de l'élément la représentation vectorielle continue avec son état JavaScript. Une raison pour laquelle vous pourriez vouloir reflètent une propriété, de sorte que le style défini par l'utilisateur s'applique lorsque l'état JS change.

Rappelez-vous notre <app-drawer>. Un consommateur de ce composant peut vouloir le masquer en fondu et/ou empêcher toute interaction de l'utilisateur lorsqu'elle est désactivée:

app-drawer[disabled] {
  opacity: 0.5;
  pointer-events: none;
}

Lorsque la propriété disabled est modifiée en JavaScript, cet attribut doit être ajouté au DOM pour que le sélecteur de l'utilisateur corresponde. L'élément peut fournir que comportement en reflétant la valeur d'un attribut du même nom:

get disabled() {
  return this.hasAttribute('disabled');
}

set disabled(val) {
  // Reflect the value of `disabled` as an attribute.
  if (val) {
    this.setAttribute('disabled', '');
  } else {
    this.removeAttribute('disabled');
  }
  this.toggleDrawer();
}

Observer les modifications apportées aux attributs

Les attributs HTML sont un moyen pratique pour les utilisateurs de déclarer un état initial:

<app-drawer open disabled></app-drawer>

Les éléments peuvent réagir aux modifications d'attributs en définissant un attributeChangedCallback Le navigateur appelle cette méthode pour chaque modification aux attributs listés dans le tableau observedAttributes.

class AppDrawer extends HTMLElement {
  // ...

  static get observedAttributes() {
    return ['disabled', 'open'];
  }

  get disabled() {
    return this.hasAttribute('disabled');
  }

  set disabled(val) {
    if (val) {
      this.setAttribute('disabled', '');
    } else {
      this.removeAttribute('disabled');
    }
  }

  // Only called for the disabled and open attributes due to observedAttributes
  attributeChangedCallback(name, oldValue, newValue) {
    // When the drawer is disabled, update keyboard/screen reader behavior.
    if (this.disabled) {
      this.setAttribute('tabindex', '-1');
      this.setAttribute('aria-disabled', 'true');
    } else {
      this.setAttribute('tabindex', '0');
      this.setAttribute('aria-disabled', 'false');
    }
    // TODO: also react to the open attribute changing.
  }
}

Dans l'exemple, nous définissons des attributs supplémentaires sur <app-drawer> lorsqu'un L'attribut disabled a été modifié. Bien que nous ne le fassions pas ici, vous pouvez utilisez également attributeChangedCallback pour synchroniser une propriété JavaScript avec son .

Mises à niveau d'éléments

Code HTML progressivement amélioré

Nous avons déjà vu que les éléments personnalisés sont définis en appelant customElements.define() Cela ne signifie pas pour autant que vous devez définir et enregistrer d'un élément personnalisé.

Les éléments personnalisés peuvent être utilisés avant que leur définition ne soit enregistrée.

L'amélioration progressive est une fonctionnalité d'éléments personnalisés. En d'autres termes, vous pouvez déclarer un ensemble d'éléments <app-drawer> sur la page et ne jamais appeler customElements.define('app-drawer', ...) et bien plus tard. En effet, le navigateur traite les éléments personnalisés potentiels différemment en raison de la valeur inconnu tags. Processus consistant à appeler define() et à attribuer un élément avec une définition de classe s'appelle "mises à niveau d'éléments".

Pour savoir quand le nom d'une balise est défini, vous pouvez utiliser window.customElements.whenDefined() Elle renvoie une promesse qui se résout lorsque le est défini.

customElements.whenDefined('app-drawer').then(() => {
  console.log('app-drawer defined');
});

Exemple : retarder le travail jusqu'à ce qu'un ensemble d'éléments enfants soit mis à niveau

<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>
// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map((socialButton) => {
  return customElements.whenDefined(socialButton.localName);
});

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});

Contenu défini par l'élément

Les éléments personnalisés peuvent gérer leur propre contenu à l'aide des API DOM qu'ils contiennent. du code de l'élément. Les réactions sont très utiles à cet effet.

Exemple – Créez un élément avec du code HTML par défaut:

customElements.define('x-foo-with-markup', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
  }
  // ...
});

La déclaration de cette balise produit:

<x-foo-with-markup>
  <b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>

// À FAIRE: DevSite - Exemple de code supprimé, car il utilisait des gestionnaires d'événements intégrés

Créer un élément qui utilise Shadow DOM

Shadow DOM permet à un élément de posséder, d'afficher et de styliser un fragment de un DOM distinct du reste de la page. Mince, vous pourriez même cacher un l'intégralité de l'application à l'aide d'une seule balise:

<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>

Pour utiliser un Shadow DOM dans un élément personnalisé, appelez this.attachShadow dans votre constructor:

let tmpl = document.createElement('template');
tmpl.innerHTML = `
  <style>:host { ... }</style> <!-- look ma, scoped styles -->
  <b>I'm in shadow dom!</b>
  <slot></slot>
`;

customElements.define('x-foo-shadowdom', class extends HTMLElement {
  constructor() {
    super(); // always call super() first in the constructor.

    // Attach a shadow root to the element.
    let shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.appendChild(tmpl.content.cloneNode(true));
  }
  // ...
});

Exemple d'utilisation :

<x-foo-shadowdom>
  <p><b>User's</b> custom text</p>
</x-foo-shadowdom>

<!-- renders as -->
<x-foo-shadowdom>
  #shadow-root
  <b>I'm in shadow dom!</b>
  <slot></slot> <!-- slotted content appears here -->
</x-foo-shadowdom>

Texte personnalisé de l'utilisateur

// À FAIRE: DevSite - Exemple de code supprimé, car il utilisait des gestionnaires d'événements intégrés

Créer des éléments à partir d'un <template>

Pour les utilisateurs inconnus, le <template> vous permet de déclarer des fragments du DOM qui sont analysés, inertes lors du chargement de la page et peuvent être activées ultérieurement au moment de l'exécution. Il s'agit d'une autre primitive de l'API sur le Web. la famille de composants. Les modèles constituent un espace réservé idéal pour déclarer la structure d'un élément personnalisé.

Exemple:enregistrer un élément avec du contenu Shadow DOM créé à partir d'un <template>:

<template id="x-foo-from-template">
  <style>
    p { color: green; }
  </style>
  <p>I'm in Shadow DOM. My markup was stamped from a &lt;template&gt;.</p>
</template>

<script>
  let tmpl = document.querySelector('#x-foo-from-template');
  // If your code is inside of an HTML Import you'll need to change the above line to:
  // let tmpl = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');

  customElements.define('x-foo-from-template', class extends HTMLElement {
    constructor() {
      super(); // always call super() first in the constructor.
      let shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(tmpl.content.cloneNode(true));
    }
    // ...
  });
</script>

Ces quelques lignes de code sont particulièrement intéressantes. Voyons les principaux éléments sur:

  1. Nous définissons un nouvel élément dans le code HTML: <x-foo-from-template>
  2. Le Shadow DOM de l'élément est créé à partir d'un <template>.
  3. Le DOM de l'élément est propre à l'élément grâce au Shadow DOM
  4. Le code CSS interne de l'élément est limité à celui-ci grâce au Shadow DOM

Je suis dans Shadow DOM. Mon balisage est basé sur un élément <template>.

// À FAIRE: DevSite - Exemple de code supprimé, car il utilisait des gestionnaires d'événements intégrés

Appliquer un style à un élément personnalisé

Même si votre élément définit son propre style à l'aide de Shadow DOM, les utilisateurs peuvent appliquer votre élément personnalisé sur leur page. Ceux-ci sont appelés "styles définis par l'utilisateur".

<!-- user-defined styling -->
<style>
  app-drawer {
    display: flex;
  }
  panel-item {
    transition: opacity 400ms ease-in-out;
    opacity: 0.3;
    flex: 1;
    text-align: center;
    border-radius: 50%;
  }
  panel-item:hover {
    opacity: 1.0;
    background: rgb(255, 0, 255);
    color: white;
  }
  app-panel > panel-item {
    padding: 5px;
    list-style: none;
    margin: 0 7px;
  }
</style>

<app-drawer>
  <panel-item>Do</panel-item>
  <panel-item>Re</panel-item>
  <panel-item>Mi</panel-item>
</app-drawer>

Vous vous demandez peut-être comment fonctionne la spécificité CSS si l'élément a des styles défini dans Shadow DOM. En termes de spécificité, les styles d’utilisateur l’emportent. Il remplacez toujours le style défini par l'élément. Consultez la section Créer un élément qui utilise le Shadow DOM.

Préconfigurer des éléments non enregistrés

Avant de mettre à jour un élément, vous pouvez le cibler en CSS à l'aide de la méthode Pseudo-classe :defined. Cela est utile pour préstyliser un composant. Pour Par exemple, vous pouvez empêcher la mise en page ou tout autre élément visuel FOUC en masquant les données des composants et de les faire apparaître en fondu lorsqu'ils sont définis.

Exemple – Masquer la valeur <app-drawer> avant qu'elle soit définie:

app-drawer:not(:defined) {
  /* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
  display: inline-block;
  height: 100vh;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

Une fois <app-drawer> défini, le sélecteur (app-drawer:not(:defined)) ne correspond plus.

Extension des éléments

L'API Custom Elements est utile pour créer des éléments HTML, utile pour étendre d'autres éléments personnalisés ou même le code HTML intégré au navigateur.

Étendre un élément personnalisé

Pour étendre un autre élément personnalisé, vous devez étendre sa définition de classe.

Exemple – Créez <fancy-app-drawer> qui étend <app-drawer>:

class FancyDrawer extends AppDrawer {
  constructor() {
    super(); // always call super() first in the constructor. This also calls the extended class' constructor.
    // ...
  }

  toggleDrawer() {
    // Possibly different toggle implementation?
    // Use ES2015 if you need to call the parent method.
    // super.toggleDrawer()
  }

  anotherMethod() {
    // ...
  }
}

customElements.define('fancy-app-drawer', FancyDrawer);

Étendre les éléments HTML natifs

Imaginons que vous souhaitiez créer un <button> plus sophistiqué. Au lieu de répliquer le comportement et les fonctionnalités de <button>, une meilleure option consiste à améliorer l'élément existant à l'aide d'éléments personnalisés.

Un élément intégré personnalisé est un élément personnalisé qui étend l'un des les tags HTML intégrés au navigateur. Le principal avantage de l'extension est d'obtenir toutes ses fonctionnalités (propriétés DOM, méthodes, accessibilité). Il n'existe pas de meilleure façon de rédiger un Web progressif application que d'améliorer progressivement le code HTML existant .

Pour étendre un élément, vous devez créer une définition de classe qui hérite dans la bonne interface DOM. Par exemple, un élément personnalisé qui s'étend <button> doit hériter de HTMLButtonElement au lieu de HTMLElement. De même, un élément qui étend <img> doit étendre HTMLImageElement.

Exemple – Extension de <button>:

// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class FancyButton extends HTMLButtonElement {
  constructor() {
    super(); // always call super() first in the constructor.
    this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY));
  }

  // Material design ripple animation.
  drawRipple(x, y) {
    let div = document.createElement('div');
    div.classList.add('ripple');
    this.appendChild(div);
    div.style.top = `${y - div.clientHeight/2}px`;
    div.style.left = `${x - div.clientWidth/2}px`;
    div.style.backgroundColor = 'currentColor';
    div.classList.add('run');
    div.addEventListener('transitionend', (e) => div.remove());
  }
}

customElements.define('fancy-button', FancyButton, {extends: 'button'});

Notez que l'appel à define() change légèrement lors de l'extension d'une annonce native. . Le troisième paramètre, qui est obligatoire, indique au navigateur l'extension. Cette étape est nécessaire, car de nombreuses balises HTML partagent le même DOM de commande. <section>, <address> et <em> (entre autres) partagent tous HTMLElement; <q> et <blockquote> partagent tous deux HTMLQuoteElement ; etc. En spécifiant {extends: 'blockquote'}, vous indiquez au navigateur que vous créez un <blockquote> a augmenté au lieu d'une <q>. Consultez le code HTML caractéristiques techniques pour obtenir la liste complète des interfaces DOM HTML.

Les utilisateurs d'un élément intégré personnalisé peuvent l'utiliser de différentes manières. Ils peuvent Déclarez-la en ajoutant l'attribut is="" à la balise native:

<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>

créez une instance en JavaScript:

// Custom elements overload createElement() to support the is="" attribute.
let button = document.createElement('button', {is: 'fancy-button'});
button.textContent = 'Fancy button!';
button.disabled = true;
document.body.appendChild(button);

Vous pouvez également utiliser l'opérateur new:

let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;

Voici un autre exemple qui étend <img>.

Exemple – Extension de <img>:

customElements.define('bigger-img', class extends Image {
  // Give img default size if users don't specify.
  constructor(width=50, height=50) {
    super(width * 10, height * 10);
  }
}, {extends: 'img'});

Les utilisateurs déclarent ce composant en tant que:

<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">

ou créez une instance en JavaScript:

const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);

Détails divers

Éléments inconnus et éléments personnalisés non définis

Le langage HTML est indulgent et flexible. Par exemple, déclarez <randomtagthatdoesntexist> sur une page et le navigateur est parfaitement satisfait de l'accepter. Pourquoi les tags non standards fonctionnent-ils ? La réponse est le HTML caractéristiques le permet. Les éléments qui ne sont pas définis par la spécification sont analysés comme HTMLUnknownElement

Il en va de même pour les éléments personnalisés. Les éléments personnalisés potentiels sont analysés en tant que HTMLElement s'ils sont créés avec un nom valide (inclut un "-"). Toi vous pouvez le vérifier dans un navigateur compatible avec les éléments personnalisés. Démarrez la console: Ctrl+Maj+J (ou Cmd+Opt+J sur Mac) et coller la les lignes de code suivantes:

// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true

// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true

Documentation de référence de l'API

Le global customElements définit des méthodes utiles pour utiliser des éléments.

define(tagName, constructor, options)

Définit un nouvel élément personnalisé dans le navigateur.

Exemple

customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
    'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});

get(tagName)

Si un nom de balise d'élément personnalisé valide est fourni, renvoie le constructeur de l'élément. Renvoie undefined si aucune définition d'élément n'a été enregistrée.

Exemple

let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();

whenDefined(tagName)

Affiche une promesse qui se résout lorsque l'élément personnalisé est défini. Si le est déjà défini, résolvez-le immédiatement. Rejeté si le nom de la balise n'est pas un nom d'élément personnalisé valide.

Exemple

customElements.whenDefined('app-drawer').then(() => {
  console.log('ready!');
});

Historique et navigateurs compatibles

Si vous suivez les composants Web depuis les deux dernières années, vous sachez que Chrome 36 ou version ultérieure a mis en œuvre une version de l'API Custom Elements qui utilise document.registerElement() au lieu de customElements.define(). C'est maintenant considéré comme une version obsolète de la norme, appelée v0. customElements.define() est la nouvelle tendance et les fournisseurs de navigateurs à mettre en œuvre. Il s'agit de Custom Elements v1.

Si l'ancienne spécification V0 vous intéresse, consultez le document html5rocks pour en savoir plus.

Prise en charge des navigateurs

Chrome 54 (état), Safari 10.1 (état) et Firefox 63 (état) ont Custom Elements v1 Edge a commencé développement d'applications.

Pour détecter des éléments personnalisés, vérifiez l'existence des éléments suivants : window.customElements:

const supportsCustomElementsV1 = 'customElements' in window;

Polyfill

En attendant que les navigateurs soient pris en charge, polyfill autonome disponibles pour Custom Elements v1. Toutefois, nous vous recommandons d'utiliser le fichier webcomponents.js chargeur pour charger de façon optimale les polyfills des composants Web. Chargeur utilise la détection de fonctionnalités pour ne charger de manière asynchrone que les pollyfills nécessaires requise par le navigateur.

Installez-la:

npm install --save @webcomponents/webcomponentsjs

Utilisation :

<!-- Use the custom element on the page. -->
<my-element></my-element>

<!-- Load polyfills; note that "loader" will load these async -->
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js" defer></script>

<!-- Load a custom element definitions in `waitFor` and return a promise -->
<script type="module">
  function loadScript(src) {
    return new Promise(function(resolve, reject) {
      const script = document.createElement('script');
      script.src = src;
      script.onload = resolve;
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }

  WebComponents.waitFor(() => {
    // At this point we are guaranteed that all required polyfills have
    // loaded, and can use web components APIs.
    // Next, load element definitions that call `customElements.define`.
    // Note: returning a promise causes the custom elements
    // polyfill to wait until all definitions are loaded and then upgrade
    // the document in one batch, for better performance.
    return loadScript('my-element.js');
  });
</script>

Conclusion

Les éléments personnalisés constituent un nouvel outil pour définir de nouvelles balises HTML dans le navigateur et créer des composants réutilisables. Associez-les à l'autre nouvelle plate-forme primitives telles que Shadow DOM et <template>, et nous commençons à réaliser image des composants Web:

  • Utilisation multi-navigateurs (norme Web) pour créer et développer des composants réutilisables
  • Vous n'avez besoin d'aucune bibliothèque ni aucun framework pour vous lancer. Vanilla JS/HTML FTW!
  • Il fournit un modèle de programmation familier. Il s'agit simplement du DOM/CSS/HTML.
  • Compatible avec les autres nouvelles fonctionnalités de la plate-forme Web (Shadow DOM, <template> et CSS) propriétés personnalisées, etc.)
  • Étroitement intégré aux outils de développement du navigateur
  • Exploitez les fonctionnalités d'accessibilité existantes.