Bonnes pratiques concernant les éléments personnalisés

Les éléments personnalisés vous permettent de créer vos propres balises HTML. Cette liste de contrôle couvre les meilleures pratiques pour vous aider à créer des éléments de haute qualité.

Les éléments personnalisés vous permettent d'étendre le code HTML et de définir vos propres balises. Il s'agit d'un incroyablement puissante, mais elles sont aussi de bas niveau, ce qui signifie qu'il ne s'agit pas toujours déterminer la meilleure façon de mettre en œuvre votre propre élément.

Pour vous aider à créer la meilleure expérience possible, nous avons mis au point cette liste de contrôle. Il décrit toutes les choses qui, selon nous, sont nécessaires pour être est un élément personnalisé qui se comporte bien.

Checklist

Shadow DOM

Créez une racine fantôme pour encapsuler les styles.

Why? L'encapsulation des styles dans la racine fantôme de votre élément garantit qu'il fonctionnera quel que soit l'endroit où elles sont utilisées. C'est particulièrement important si un développeur souhaite placer votre élément à l’intérieur de la racine de l’ombre d’un autre élément. Ce s'applique même aux éléments simples comme une case à cocher ou une case d'option. Il peut s'agir le seul contenu à l'intérieur de votre racine fantôme sera les styles eux-mêmes.
Exemple Le <howto-checkbox>.

Créez la racine fantôme dans le constructeur.

Why? On parle de constructeur lorsque vous avez une connaissance exclusive de votre élément. C'est le moment idéal pour définir les détails de l'implémentation que vous ne souhaitez pas que d'autres avec d'autres éléments. Cette opération est effectuée dans un rappel ultérieur, par exemple connectedCallback, signifie que vous devez vous protéger situations où votre élément est détaché, puis réassocié au document.
Exemple Le <howto-checkbox>.

Placez tous les enfants créés par l'élément dans sa racine fantôme.

Why? Les enfants créés par votre élément font partie de son implémentation et doivent être privé. Sans la protection d'une racine fantôme, le code JavaScript externe peut interférer par inadvertance avec ces enfants.
Exemple Le <howto-tabs>.

Utiliser <slot> pour projeter les enfants du Light DOM dans votre Shadow DOM

Why? Autorisez les utilisateurs de votre composant à spécifier du contenu dans votre composant, car les éléments enfants HTML rendent votre composant plus composable. Lorsqu'un navigateur n'accepte pas les éléments personnalisés, le contenu imbriqué reste disponible, visible et accessible.
Exemple Le <howto-tabs>.

Définissez un style d'affichage :host (par exemple, block, inline-block, flex), sauf si vous préférez la valeur par défaut inline

Why? Les éléments personnalisés étant display: inline par défaut, vous devez définir leur width ou height n'auront aucun effet. Souvent surprend les développeurs et peut causer des problèmes liés à la mise en page. À moins que vous ne préfériez un écran inline, vous doit toujours définir une valeur display par défaut.
Exemple Le <howto-checkbox>.

Ajoutez un style d'affichage :host qui respecte l'attribut masqué.

Why? Un élément personnalisé avec un style display par défaut (par exemple, :host { display: block }, remplacera la valeur de précision inférieure intégré <ph type="x-smartling-placeholder"></ph> hidden. Vous serez peut-être surpris si vous pensez définir les hidden sur votre élément pour l'afficher display: none. De plus, à un style display par défaut, ajoutez la prise en charge de hidden avec :host([hidden]) { display: none }.
Exemple Le <howto-checkbox>.

Attributs et propriétés

Ne remplacez pas les attributs globaux définis par l'auteur.

Why? Les attributs globaux sont ceux qui sont présents dans tous les éléments HTML. Un peu par exemple tabindex et role. Un élément personnalisé nous vous conseillons de définir son tabindex initial sur 0 pour que ce soit le cas sélectionnables. Mais vous devez toujours vérifier d'abord si le développeur qui utilise votre élément l'a défini sur une autre valeur. Si, par exemple, il a défini tabindex à -1, cela indique qu'ils ne souhaitent pas que la pour qu'il soit interactif.
Exemple Le <howto-checkbox>. Pour en savoir plus, consultez Ne remplacez pas l'auteur de la page.

Toujours accepter les données primitives (chaînes, nombres, valeurs booléennes) en tant qu'attributs ou propriétés.

Why? Les éléments personnalisés, comme leurs homologues intégrés, doivent être configurables. La configuration peut être transmise de manière déclarative, via des attributs ou de manière impérative via des propriétés JavaScript. Idéalement, chaque attribut doit également être lié à une propriété correspondante.
Exemple Le <howto-checkbox>.

Efforcez-vous de synchroniser les attributs et les propriétés des données primitifs, à l'attribut, et inversement.

Why? Vous ne savez jamais comment un utilisateur interagira avec votre élément. Il pourrait définir une propriété en JavaScript, puis s'attendre à lire cette valeur à l'aide d'une API comme getAttribute(). Si chaque attribut a une valeur la propriété correspondante, et que ces deux éléments reflètent, pour que les utilisateurs travaillent avec votre élément. Autrement dit, appeler setAttribute('foo', value) doit également définir une valeur foo, et inversement. Il existe, bien sûr, des exceptions cette règle. Vous ne devez pas refléter les propriétés de fréquence élevée, comme currentTime dans un lecteur vidéo. Faites appel à votre bon sens. Si semble qu'un utilisateur interagira avec une propriété ou un attribut ; qu'il n'est pas gênant de la réfléchir, alors faites-le.
Exemple Le <howto-checkbox>. Pour en savoir plus, consultez Évitez les problèmes de réentrée.

Essayez de n'accepter que les données enrichies (objets, tableaux) en tant que propriétés.

Why? De manière générale, il n'existe aucun exemple d'élément HTML intégré qui acceptent des données enrichies (tableaux et objets JavaScript simples) via leur . Les données enrichies sont acceptées par le biais d'appels de méthode ou propriétés. L'acceptation des données enrichies en tant que source de données : la sérialisation d'un objet volumineux en chaîne peut s'avérer coûteuse. toutes les références d'objets seront perdues lors de ce processus de concaténation. Pour exemple, si vous chaînez un objet qui fait référence à un autre objet, ou un nœud DOM, ces références seront perdues.

Ne pas refléter les propriétés des données enrichies dans les attributs.

Why? La réplication des propriétés de données enrichies dans des attributs est inutilement coûteuse. nécessitant la sérialisation et la désérialisation des mêmes objets JavaScript. À moins que vous avez un cas d'utilisation qui ne peut être résolu qu'avec cette fonctionnalité, de l'éviter.

Pensez à vérifier les propriétés qui ont pu être définies avant l'élément mises à niveau.

Why? Un développeur qui utilise votre élément peut tenter d'en définir une propriété. avant le chargement de sa définition. Cela est particulièrement vrai si le développeur utilise un framework qui gère le chargement des composants, leur estampe à la page et lier leurs propriétés à un modèle.
Exemple Le <howto-checkbox>. Pour en savoir plus, consultez Rendre les propriétés différées.

N'appliquez pas vous-même les cours.

Why? Les éléments qui doivent exprimer leur état doivent le faire à l'aide d'attributs. La L'attribut class est généralement considéré comme appartenant au développeur utilisant votre élément. Si vous l'écrivez vous-même, vous risquez piétiner les classes de développeurs.

Événements

Distribuer des événements en réponse à l'activité des composants internes

Why? Le composant peut avoir des propriétés qui changent en réponse à une activité qui seul votre composant est informé, par exemple, si un minuteur ou une animation ou le chargement d'une ressource est terminé. Il est utile d'envoyer des événements en réponse à ces modifications pour avertir l'hôte que l'état du composant est différentes.

Ne envoyez pas d'événements en réponse à la définition d'une propriété au niveau de l'hôte (vers le bas flux de données).

Why? Il n'est plus nécessaire d'envoyer un événement en réponse à un paramètre d'hôte. (l'hôte connaît l'état actuel car il vient de le définir). Événements de distribution En réponse à un paramètre d'hôte, une propriété peut provoquer des boucles infinies avec les données des systèmes de liaison.
Exemple Le <howto-checkbox>.

Vidéo explicative

Ne pas remplacer l'auteur de la page

Il est possible qu'un développeur utilisant votre élément veuille remplacer certaines son état initial. Par exemple, si vous modifiez son role ARIA ou sa mise au point avec tabindex Vérifiez si ces attributs et d'autres attributs globaux ont été définis. avant d'appliquer vos propres valeurs.

connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', 0);

Rendre les propriétés inactives

Un développeur peut tenter de définir une propriété sur votre élément avant ses a été chargée. Cela est particulièrement vrai si le développeur utilise qui gère le chargement des composants, les insère dans la page et de lier leurs propriétés à un modèle.

Dans l'exemple suivant, Angular lie de manière déclarative les isChecked à la propriété checked de la case à cocher. Si la définition de Howto-box a été chargé en différé. Il est possible qu'Angular tente de définir la propriété cochée avant la mise à jour de l'élément.

<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>

Un élément personnalisé doit gérer ce scénario en vérifiant si des propriétés ont des déjà défini sur son instance. <howto-checkbox> illustre ce modèle à l'aide d'une méthode appelée _upgradeProperty().

connectedCallback() {
  ...
  this._upgradeProperty('checked');
}

_upgradeProperty(prop) {
  if (this.hasOwnProperty(prop)) {
    let value = this[prop];
    delete this[prop];
    this[prop] = value;
  }
}

_upgradeProperty() capture la valeur de l'instance non mise à niveau et supprime la propriété afin qu'elle n'occulte pas le setter de la propriété de l'élément personnalisé. Ainsi, lorsque le chargement de la définition de l'élément est terminé, il peut immédiatement reflètent l'état correct.

Éviter les problèmes de réentrée

Il est tentant d'utiliser attributeChangedCallback() pour refléter l'état dans une sous-jacente. Exemple:

// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'checked')
    this.checked = newValue;
}

Mais cela peut créer une boucle infinie si le setter de la propriété reflète également l'attribut.

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    // OOPS! This will cause an infinite loop because it triggers the
    // attributeChangedCallback() which then sets this property again.
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

Une alternative consiste à autoriser le setter de propriété à refléter l'attribut, et pour que le getter détermine sa valeur en fonction de l'attribut.

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

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

Dans cet exemple, l'ajout ou la suppression de l'attribut définit également la propriété.

Enfin, attributeChangedCallback() peut être utilisé pour gérer les effets secondaires. comme l'application d'états ARIA.

attributeChangedCallback(name, oldValue, newValue) {
  const hasValue = newValue !== null;
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-checked', hasValue);
      break;
    ...
  }
}