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.
Lista de tareas
Shadow DOM
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;
...
}
}