Prácticas recomendadas para los elementos personalizados

Los elementos personalizados te permiten crear tus propias etiquetas HTML. Esta lista de tareas abarca prácticas recomendadas para ayudarte a crear elementos de alta calidad.

Los elementos personalizados te permiten extender el código HTML y definir tus propias etiquetas. Son un función increíblemente potente, pero también son de bajo nivel, lo que significa que no es siempre esté claro cuál es la mejor manera de implementar tu propio elemento.

Para ayudarte a crear las mejores experiencias posibles, reunimos esta lista de tareas. Desglosa todo lo que creemos que hace falta elemento personalizado con buen comportamiento.

Crea una shadow root para encapsular estilos.

¿Por qué? El encapsulamiento de estilos en la shadow root de tu elemento garantiza que funcione sin importar dónde se usen. Esto es muy importante si un desarrollador desea colocar tu elemento dentro de la shadow root de otro elemento. Esta se aplica incluso a elementos simples, como una casilla de verificación o un botón de selección. Podría ser en el caso de que el único contenido dentro de tu shadow root sean los estilos ellos mismos.
Ejemplo El elemento <howto-checkbox>.

Crea tu shadow root en el constructor.

¿Por qué? El constructor es cuando tienes conocimiento exclusivo de tu elemento. Es un buen momento para configurar los detalles de implementación que no quieres que otros elementos con los que se están mezclando. Hacer esto en una devolución de llamada posterior, como la connectedCallback significa que deberás protegerte de situaciones en las que tu elemento se separa y, luego, se vuelve a adjuntar al documento.
Ejemplo El elemento <howto-checkbox>.

Coloca los elementos secundarios que el elemento cree en su shadow root.

¿Por qué? Los elementos secundarios que crea tu elemento forman parte de su implementación y deben privada. Sin la protección de una shadow root, fuera de JavaScript interferir inadvertidamente con estos niños.
Ejemplo El elemento <howto-tabs>.

Usar <slot> para proyectar elementos secundarios del Light DOM en tu shadow DOM

¿Por qué? Permitir que los usuarios de tu componente especifiquen contenido en él como elementos secundarios HTML hace que este sea más componible. Cuando un navegador no admite elementos personalizados, el contenido anidado permanece disponible, es visible y se puede acceder a él.
Ejemplo El elemento <howto-tabs>.

Establece un estilo de visualización :host (p.ej., block, inline-block, flex), a menos que prefieras la configuración predeterminada de inline

¿Por qué? Los elementos personalizados son display: inline de forma predeterminada, por lo que establecer su width o height no tendrán efecto. Esto a menudo sorprende a los desarrolladores y puede causar problemas relacionados con diseñar la página. A menos que prefieras una pantalla inline, siempre debes establecer un valor predeterminado de display.
Ejemplo El elemento <howto-checkbox>.

Agrega un estilo de visualización :host que respete el atributo oculto.

¿Por qué? Un elemento personalizado con un estilo display predeterminado, p.ej., :host { display: block }, anulará la especificidad más baja integrado hidden. Es posible que te sorprenda si esperas establecer la hidden. en tu elemento para renderizarlo display: none. Además, a un estilo display predeterminado, agrega compatibilidad con hidden con :host([hidden]) { display: none }.
Ejemplo El elemento <howto-checkbox>.

Atributos y propiedades

No anules los atributos globales establecidos por el autor.

¿Por qué? Los atributos globales son aquellos que están presentes en todos los elementos HTML. Algunos algunos ejemplos incluyen tabindex y role. Un elemento personalizado es posible que desee establecer su tabindex inicial en 0 para que sea de teclado enfocable. Pero siempre debes verificar primero si el desarrollador que usa tu elemento estableció esto en otro valor. Si, por ejemplo, se estableció De tabindex a -1, esto es un indicador de que no desea que el elemento para que sea interactivo.
Ejemplo El elemento <howto-checkbox>. Esto se explica con más detalle en No anules el autor de la página.

Aceptar siempre datos primitivos (cadenas, números, booleanos) como cualquiera de los atributos o propiedades.

¿Por qué? Los elementos personalizados, al igual que sus equivalentes integrados, deben poder configurarse. La configuración se puede pasar de forma declarativa, a través de atributos o de forma imperativa a través de las propiedades de JavaScript. Idealmente, todos los atributos también deben estar vinculados a una propiedad correspondiente.
Ejemplo El elemento <howto-checkbox>.

Tiene como objetivo mantener sincronizados los atributos y las propiedades de datos primitivos, a atribuir y viceversa.

¿Por qué? Nunca se sabe cómo un usuario interactuará con tu elemento. Quizás establecer una propiedad en JavaScript y, luego, esperar leer ese valor con una API como getAttribute(). Si cada atributo tiene un propiedad correspondiente y ambas se reflejan, para que sea más fácil para usuarios a trabajar con tu elemento. En otras palabras, llamar setAttribute('foo', value) también debe establecer un valor propiedad foo y viceversa. Por supuesto, existen excepciones a esta regla. No deberías reflejar propiedades de alta frecuencia, p.ej., currentTime en un reproductor de video Usa tu mejor criterio. Si Parece que un usuario interactuará con una propiedad o un atributo. reflejarlo no es tedioso, entonces hazlo.
Ejemplo El elemento <howto-checkbox>. Esto se explica con más detalle en Evita los problemas de reentrada.

Intenta aceptar solo datos enriquecidos (objetos, arrays) como propiedades.

¿Por qué? En términos generales, no hay ejemplos de elementos HTML integrados que aceptan datos enriquecidos (arrays y objetos simples de JavaScript) a través de sus atributos. En cambio, se aceptan datos enriquecidos mediante llamadas de método o propiedades. Hay algunas desventajas obvias al aceptar datos enriquecidos como atributos: puede ser costoso serializar un objeto grande en una cadena se perderán las referencias de objetos en este proceso de stringificación. Para ejemplo, si encadenas un objeto que tiene referencia a otro, o un nodo del DOM, se perderán esas referencias.

No se reflejan las propiedades de datos enriquecidos en los atributos.

¿Por qué? Reflejar las propiedades de los datos enriquecidos en atributos es innecesariamente costoso. que requieren serializar y deserializar los mismos objetos de JavaScript. Salvo que tienes un caso de uso que solo se puede resolver con esta función, probablemente sea es mejor evitarlo.

Verifica las propiedades que podrían haberse establecido antes del elemento. actualizado.

¿Por qué? Un desarrollador que usa tu elemento puede intentar establecer una propiedad en él antes de que se cargue su definición. Esto es especialmente cierto si el desarrollador usa un framework que controla la carga de componentes y los sella a la página y vincular sus propiedades a un modelo.
Ejemplo El elemento <howto-checkbox>. Explicación más detallada en Haz que las propiedades sean diferidas.

No apliques clases por tu cuenta.

¿Por qué? Los elementos que necesitan expresar su estado deben hacerlo mediante atributos. El Por lo general, el atributo class pertenece a la desarrollador usando tu elemento y escribir en él tú mismo puede para pisar fuerte las clases de desarrolladores.

Eventos

Eventos de despacho en respuesta a la actividad de componentes internos.

¿Por qué? Tu componente puede tener propiedades que cambian en respuesta a una actividad que que solo el componente conoce, por ejemplo, si un temporizador o una animación se complete o termine de cargarse un recurso. Es útil enviar eventos en respuesta a estos cambios para notificar al host que el estado del componente es es diferente.

No despaches eventos en respuesta a la configuración del host en una propiedad (hacia abajo) flujo de datos).

¿Por qué? Enviar un evento en respuesta a la configuración del host de una propiedad es superfluo. (el host conoce el estado actual porque simplemente lo establece). Eventos de envío En respuesta a una configuración de host, una propiedad puede causar bucles infinitos de datos. de enlace de red.
Ejemplo El elemento <howto-checkbox>.

Explicaciones

No anular el autor de la página

Es posible que un desarrollador que usa tu elemento quiera anular algunos de a su estado inicial. Por ejemplo, cambiar su role de ARIA o la enfocabilidad con tabindex Verifica si se establecieron estos y otros atributos globales. antes de aplicar tus propios valores.

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

Haz que las propiedades sean diferidas

Un desarrollador puede intentar establecer una propiedad en tu elemento antes de su se cargó la definición. Esto es especialmente cierto si el desarrollador usa un framework que controla la carga de componentes, su inserción en la página y que vinculan sus propiedades a un modelo.

En el siguiente ejemplo, Angular vincula declarativamente los atributos la propiedad isChecked a la propiedad checked de la casilla de verificación. Si la definición de la casilla de verificación del instructivo se cargó de forma diferida, es posible que Angular intente configurar la propiedad marcada antes de que se actualice el elemento.

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

Un elemento personalizado debería manejar esta situación verificando si alguna propiedad tiene ya se establecieron en su instancia. La <howto-checkbox> demuestra este patrón mediante un método llamado _upgradeProperty().

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

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

_upgradeProperty() captura el valor de la instancia no actualizada y borra la propiedad para que no oculte el propio establecedor de propiedades del elemento personalizado. De esta manera, cuando finalmente se carga la definición del elemento, puede reflejan el estado correcto.

Evita problemas de reentrada

Resulta tentador usar attributeChangedCallback() para reflejar el estado en un propiedad subyacente, por ejemplo:

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

Pero esto puede crear un bucle infinito si el establecedor de propiedades también se refleja en el atributo.

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');
}

Una alternativa es permitir que el establecedor de propiedades se refleje en el atributo. el método get determine su valor según el atributo.

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

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

En este ejemplo, agregar o quitar el atributo también establecerá la propiedad.

Por último, attributeChangedCallback() se puede usar para controlar efectos secundarios como aplicar estados de 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;
    ...
  }
}