Os elementos personalizados permitem que os desenvolvedores da Web definam novas tags HTML, ampliem as existentes e criem componentes da Web reutilizáveis.
Com os elementos personalizados, os desenvolvedores da Web podem criar novas tags HTML, melhorar as tags HTML existentes ou estender os componentes criados por outros desenvolvedores. A API é a base dos componentes da Web. Ele traz uma maneira baseada em padrões da Web para criar componentes reutilizáveis usando apenas JS/HTML/CSS vanilla. O resultado é menos código, código modular e mais reutilização nos nossos apps.
Introdução
O navegador é uma ferramenta excelente para estruturar aplicativos da Web. Ele se chama HTML. Você já deve ter ouvido falar sobre isso. Ele é declarativo, portátil, tem suporte bom e é fácil de usar. Por mais que o HTML seja ótimo, o vocabulário e a extensibilidade dele são limitados. O padrão ativo do HTML sempre carecia de uma maneira de associar automaticamente o comportamento do JS à sua marcação... até agora.
Os elementos personalizados são a resposta para modernizar o HTML, preencher as partes ausentes e agrupar a estrutura com o comportamento. Se o HTML não fornecer a solução para um problema, podemos criar um elemento personalizado que o faça. Os elementos personalizados ensinam novos truques ao navegador, preservando os benefícios do HTML.
Como definir um novo elemento
Para definir um novo elemento HTML, precisamos do poder do JavaScript.
O customElements
global é usado para definir um elemento personalizado e ensinar
o navegador sobre uma nova tag. Chame customElements.define()
com o nome da tag
que você quer criar e um class
JavaScript que estende o HTMLElement
base.
Exemplo: definição de um painel de gaveta para dispositivos móveis, <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 {...});
Exemplo de uso:
<app-drawer></app-drawer>
É importante lembrar que o uso de um elemento personalizado não é diferente
do uso de um <div>
ou de qualquer outro elemento. As instâncias podem ser declaradas na página,
criadas dinamicamente em JavaScript, os listeners de eventos podem ser anexados etc. Continue
lendo para conferir mais exemplos.
Como definir a API JavaScript de um elemento
A funcionalidade de um elemento personalizado é definida usando um ES2015
class
que estende HTMLElement
. A extensão de HTMLElement
garante que o elemento personalizado
herde toda a API DOM e significa que todas as propriedades/métodos adicionados à
classe passam a fazer parte da interface DOM do elemento. Use a classe para
criar uma API JavaScript pública para sua tag.
Exemplo: definição da 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);
Neste exemplo, criamos uma gaveta com uma propriedade open
, uma propriedade disabled
e um método toggleDrawer()
. Ele também reflete propriedades como atributos
HTML.
Um recurso interessante dos elementos personalizados é que this
dentro de uma definição de classe
se refere ao próprio elemento DOM, ou seja, à instância da classe. No nosso
exemplo, this
se refere a <app-drawer>
. Assim (😉) o elemento pode
anexar um listener click
a si mesmo. E você não está limitado a listeners de eventos.
A API DOM inteira está disponível no código do elemento. Use this
para acessar as
propriedades do elemento, inspecionar os filhos (this.children
), consultar nós
(this.querySelectorAll('.items')
) etc.
Regras para criar elementos personalizados
- O nome de um elemento personalizado precisa conter um hífen (-). Portanto,
<x-tags>
,<my-element>
e<my-awesome-app>
são nomes válidos, enquanto<tabs>
e<foo_bar>
não são. Esse requisito é para que o analisador HTML possa distinguir elementos personalizados de elementos normais. Ele também garante a compatibilidade futura quando novas tags são adicionadas ao HTML. - Não é possível registrar a mesma tag mais de uma vez. Tentar fazer isso
gerará um
DOMException
. Depois de informar ao navegador sobre uma nova tag, é isso. Não é possível cancelar. - Os elementos personalizados não podem ser auto-fechamento porque o HTML permite apenas alguns
elementos
auto-fechamento. Sempre escreva uma tag de fechamento
(
<app-drawer></app-drawer>
).
Reações de elementos personalizados
Um elemento personalizado pode definir ganchos de ciclo de vida especiais para executar o código durante momentos interessantes da existência dele. Elas são chamadas de reações de elementos personalizados.
Nome | Chamado quando |
---|---|
constructor |
Uma instância do elemento é
criada ou upgradeada. Útil para inicializar
o estado, configurar listeners de eventos ou
criar um DOM sombra.
Consulte a
especificação
para ver as restrições do que é possível fazer no constructor .
|
connectedCallback |
É chamado toda vez que o elemento é inserido no DOM. Útil para executar o código de configuração, como buscar recursos ou renderizar. Em geral, tente adiar o trabalho até esse horário. |
disconnectedCallback |
É chamado toda vez que o elemento é removido do DOM. Útil para executar o código de limpeza. |
attributeChangedCallback(attrName, oldVal, newVal) |
É chamado quando um atributo observado foi
adicionado, removido, atualizado ou substituído. Também é chamado para valores iniciais
quando um elemento é criado pelo analisador ou
atualizado. Observação:apenas
atributos listados na propriedade observedAttributes vão
receber esse callback.
|
adoptedCallback |
O
elemento personalizado foi movido para um novo document (por exemplo,
alguém chamado document.adoptNode(el) ).
|
Os callbacks de reação são síncronos. Se alguém chamar el.setAttribute()
no seu elemento, o navegador vai chamar attributeChangedCallback()
imediatamente.
Da mesma forma, você vai receber um disconnectedCallback()
logo após o elemento ser
removido do DOM (por exemplo, quando o usuário chama el.remove()
).
Exemplo:adição de reações de elementos personalizadas a <app-drawer>
:
class AppDrawer extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
// ...
}
connectedCallback() {
// ...
}
disconnectedCallback() {
// ...
}
attributeChangedCallback(attrName, oldVal, newVal) {
// ...
}
}
Defina reações se/quando fizer sentido. Se o elemento for suficientemente complexo
e abrir uma conexão com o IndexedDB em connectedCallback()
, faça a limpeza
necessária em disconnectedCallback()
. Mas tenha cuidado. Não é possível confiar que seu
elemento será removido do DOM em todas as circunstâncias. Por exemplo,
disconnectedCallback()
nunca será chamado se o usuário fechar a guia.
Propriedades e atributos
Como refletir propriedades em atributos
É comum que as propriedades HTML reflitam o valor de volta para o DOM como um
atributo HTML. Por exemplo, quando os valores de hidden
ou id
são alterados em
JS:
div.id = 'my-id';
div.hidden = true;
os valores são aplicados ao DOM em tempo real como atributos:
<div id="my-id" hidden>
Isso é chamado de propriedades de reflexão para atributos. Quase todas as propriedades em HTML fazem isso. Por quê? Os atributos também são úteis para configurar um elemento de forma declarativa, e algumas APIs, como os seletores de acessibilidade e CSS, dependem de atributos para funcionar.
Refletir uma propriedade é útil em qualquer lugar em que você queira manter a representação do DOM do elemento em sincronia com o estado do JavaScript. Uma das razões para refletir uma propriedade é para que o estilo definido pelo usuário seja aplicado quando o estado do JS mudar.
Lembre-se de <app-drawer>
. Um consumidor desse componente pode querer desativá-lo
e/ou impedir a interação do usuário quando ele estiver desativado:
app-drawer[disabled] {
opacity: 0.5;
pointer-events: none;
}
Quando a propriedade disabled
é alterada no JS, queremos que esse atributo seja
adicionado ao DOM para que o seletor do usuário corresponda. O elemento pode fornecer esse
comportamento refletindo o valor para um atributo com o mesmo nome:
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();
}
Observar mudanças nos atributos
Os atributos HTML são uma maneira conveniente de declarar o estado inicial:
<app-drawer open disabled></app-drawer>
Os elementos podem reagir a mudanças de atributos definindo um
attributeChangedCallback
. O navegador vai chamar esse método para cada mudança
nos atributos listados na matriz 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.
}
}
No exemplo, estamos definindo outros atributos no <app-drawer>
quando um
atributo disabled
é alterado. Embora não estejamos fazendo isso aqui, você também pode
usar o attributeChangedCallback
para manter uma propriedade JS sincronizada com o
atributo.
Upgrades de elementos
HTML aprimorado progressivamente
Já aprendemos que os elementos personalizados são definidos chamando
customElements.define()
. Mas isso não significa que você precisa definir e registrar um
elemento personalizado de uma só vez.
Os elementos personalizados podem ser usados antes de a definição deles ser registrada.
O aprimoramento progressivo é um recurso de elementos personalizados. Em outras palavras, é possível
declarar vários elementos <app-drawer>
na página e nunca invocar
customElements.define('app-drawer', ...)
até muito mais tarde. Isso ocorre porque o
navegador trata os elementos personalizados em potencial de maneira diferente devido às tags
desconhecidas. O processo de chamar define()
e atribuir um elemento
existente com uma definição de classe é chamado de "upgrades de elemento".
Para saber quando um nome de tag é definido, use
window.customElements.whenDefined()
. Ele retorna uma promessa que é resolvida quando o
elemento é definido.
customElements.whenDefined('app-drawer').then(() => {
console.log('app-drawer defined');
});
Exemplo: atrasar o trabalho até que um conjunto de elementos filhos seja atualizado
<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.
});
Conteúdo definido pelo elemento
Os elementos personalizados podem gerenciar o próprio conteúdo usando as APIs DOM no código do elemento. As reações são úteis para isso.
Exemplo: crie um elemento com um HTML padrão:
customElements.define('x-foo-with-markup', class extends HTMLElement {
connectedCallback() {
this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
}
// ...
});
A declaração dessa tag vai produzir:
<x-foo-with-markup>
<b>I'm an x-foo-with-markup!</b>
</x-foo-with-markup>
// TODO: DevSite - O exemplo de código foi removido porque usava manipuladores de eventos inline.
Criar um elemento que usa o shadow DOM
O Shadow DOM oferece uma maneira de um elemento possuir, renderizar e estilizar um pedaço de DOM separado do restante da página. Você pode até ocultar um app inteiro em uma única tag:
<!-- chat-app's implementation details are hidden away in Shadow DOM. -->
<chat-app></chat-app>
Para usar o shadow DOM em um elemento personalizado, chame this.attachShadow
dentro do
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));
}
// ...
});
Exemplo de uso:
<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>
Texto personalizado do usuário
// TODO: DevSite - O exemplo de código foi removido porque usava manipuladores de eventos inline.
Como criar elementos com um <template>
Para quem não conhece, o elemento
<template>
permite declarar fragmentos do DOM que são analisados, inertes no carregamento da página e
podem ser ativados mais tarde no tempo de execução. É outra primitiva de API na família de
componentes da Web. Os modelos são um marcador de posição ideal para declarar a
estrutura de um elemento personalizado.
Exemplo:registrar um elemento com conteúdo do shadow DOM criado a partir de um
<template>
:
<template id="x-foo-from-template">
<style>
p { color: green; }
</style>
<p>I'm in Shadow DOM. My markup was stamped from a <template>.</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>
Essas poucas linhas de código são muito eficientes. Vamos entender os principais pontos em andamento:
- Estamos definindo um novo elemento em HTML:
<x-foo-from-template>
- O shadow DOM do elemento é criado a partir de um
<template>
- O DOM do elemento é local para o elemento graças ao shadow DOM
- O CSS interno do elemento é limitado ao elemento graças ao shadow DOM
Estou no Shadow DOM. Minha marcação foi feita a partir de um <template>.
// TODO: DevSite - O exemplo de código foi removido porque usava manipuladores de eventos inline.
Como definir o estilo de um elemento personalizado
Mesmo que seu elemento defina o próprio estilo usando o Shadow DOM, os usuários podem estilizar o elemento personalizado na página. Eles são chamados de "estilos definidos pelo usuário".
<!-- 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>
Você pode estar se perguntando como a especificidade do CSS funciona se o elemento tiver estilos definidos no Shadow DOM. Em termos de especificidade, os estilos do usuário são os vencedores. Eles sempre vão substituir o estilo definido pelo elemento. Consulte a seção Criar um elemento que usa o shadow DOM.
Estilo prévio de elementos não registrados
Antes que um elemento seja atualizado, é possível segmentá-lo no CSS usando a
pseudoclasse :defined
. Isso é útil para pré-estilizar um componente. Por
exemplo, você pode impedir o layout ou outro FOUC visual ocultando componentes indefinidos
e os mostrando quando eles forem definidos.
Exemplo: oculte <app-drawer>
antes que ele seja definido:
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;
}
Depois que <app-drawer>
é definido, o seletor (app-drawer:not(:defined)
)
não corresponde mais.
Como estender elementos
A API Custom Elements é útil para criar novos elementos HTML, mas também para estender outros elementos personalizados ou até mesmo o HTML integrado do navegador.
Como estender um elemento personalizado
Para estender outro elemento personalizado, estenda a definição da classe.
Exemplo: crie <fancy-app-drawer>
que estende <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);
Como estender elementos HTML nativos
Digamos que você queira criar uma <button>
mais sofisticada. Em vez de replicar o
comportamento e a funcionalidade de <button>
, uma opção melhor é aprimorar
progressivamente o elemento usando elementos personalizados.
Um elemento integrado personalizado é um elemento personalizado que estende uma das tags HTML integradas do navegador. O principal benefício de estender um elemento já existente é ter acesso a todos os recursos dele (propriedades DOM, métodos, acessibilidade). Não há uma maneira melhor de escrever um app Web progressivo do que melhorar progressivamente os elementos HTML atuais.
Para estender um elemento, você precisa criar uma definição de classe que herda
da interface DOM correta. Por exemplo, um elemento personalizado que estende
<button>
precisa herdar de HTMLButtonElement
em vez de HTMLElement
.
Da mesma forma, um elemento que estende <img>
precisa estender HTMLImageElement
.
Exemplo: como estender <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'});
A chamada para define()
muda um pouco ao estender um elemento
nativo. O terceiro parâmetro obrigatório informa ao navegador qual tag você está
estendendo. Isso é necessário porque muitas tags HTML compartilham a mesma interface
DOM. <section>
, <address>
e <em>
(entre outros) compartilham
HTMLElement
; <q>
e <blockquote>
compartilham HTMLQuoteElement
; etc.
Especificar {extends: 'blockquote'}
informa ao navegador que você está criando uma
<blockquote>
aprimorada em vez de uma <q>
. Consulte a especificação
do HTML
para conferir a lista completa de interfaces DOM do HTML.
Os consumidores de um elemento integrado personalizado podem usá-lo de várias maneiras. Eles podem
declarar isso adicionando o atributo is=""
à tag nativa:
<!-- This <button> is a fancy button. -->
<button is="fancy-button" disabled>Fancy button!</button>
Crie uma instância em 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);
ou use o operador new
:
let button = new FancyButton();
button.textContent = 'Fancy button!';
button.disabled = true;
Confira outro exemplo que estende <img>
.
Exemplo: como estender <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'});
Os usuários declaram esse componente como:
<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">
ou crie uma instância em 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);
Detalhes diversos
Elementos desconhecidos x elementos personalizados indefinidos
O HTML é flexível e fácil de trabalhar. Por exemplo, declare
<randomtagthatdoesntexist>
em uma página, e o navegador a aceita
sem problemas. Por que as tags não padronizadas funcionam? A resposta é que a especificação
HTML
permite isso. Os elementos que não são definidos pela especificação são analisados como
HTMLUnknownElement
.
Isso não é verdade para elementos personalizados. Elementos personalizados em potencial são analisados
como HTMLElement
se forem criados com um nome válido (que inclua um "-"). Você
pode verificar isso em um navegador que ofereça suporte a elementos personalizados. Ative o console:
Ctrl+Shift+J (ou Cmd+Opt+J no Mac) e cole as
seguintes linhas de código:
// "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
Referência da API
O customElements
global define métodos úteis para trabalhar com elementos
personalizados.
define(tagName, constructor, options)
Define um novo elemento personalizado no navegador.
Exemplo
customElements.define('my-app', class extends HTMLElement { ... });
customElements.define(
'fancy-button', class extends HTMLButtonElement { ... }, {extends: 'button'});
get(tagName)
Dado um nome de tag de elemento personalizado válido, retorna o construtor do elemento.
Retorna undefined
se nenhuma definição de elemento tiver sido registrada.
Exemplo
let Drawer = customElements.get('app-drawer');
let drawer = new Drawer();
whenDefined(tagName)
Retorna uma promessa que é resolvida quando o elemento personalizado é definido. Se o elemento já estiver definido, resolva imediatamente. Rejeita se o nome da tag não for um nome de elemento personalizado válido.
Exemplo
customElements.whenDefined('app-drawer').then(() => {
console.log('ready!');
});
Histórico e suporte a navegadores
Se você acompanha os componentes da Web há alguns anos, sabe que o Chrome 36 e versões mais recentes implementaram uma versão da API Custom Elements que usa
document.registerElement()
em vez de customElements.define()
. Ela agora é
considerada uma versão descontinuada do padrão, chamada v0.
customElements.define()
é a nova tendência e o que os fornecedores de navegadores estão
começando a implementar. Ela se chama Elementos personalizados v1.
Se você tiver interesse na especificação v0 antiga, confira o artigo html5rocks.
Suporte ao navegador
O Chrome 54 (status), o Safari 10.1 (status) e o Firefox 63 (status) têm a versão 1 dos elementos personalizados. O Edge já começou a ser desenvolvido.
Para detectar elementos personalizados, verifique a existência de
window.customElements
:
const supportsCustomElementsV1 = 'customElements' in window;
Polyfill
Até que o suporte do navegador esteja amplamente disponível, há um polyfill autônomo disponível para Elementos personalizados v1. No entanto, recomendamos usar o webcomponents.js loader para carregar da melhor forma possível os polyfills de componentes da Web. O loader usa a detecção de recursos para carregar de forma assíncrona apenas os pollyfills necessários requeridos pelo navegador.
Instale:
npm install --save @webcomponents/webcomponentsjs
Uso:
<!-- 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>
Conclusão
Os elementos personalizados oferecem uma nova ferramenta para definir novas tags HTML no navegador e
criar componentes reutilizáveis. Combine-os com as outras primitivas de plataforma
novas, como o Shadow DOM e o <template>
, e você vai começar a entender o panorama
geral dos componentes da Web:
- Entre navegadores (padrão da Web) para criar e estender componentes reutilizáveis.
- Não requer biblioteca ou framework para começar. Vanilla JS/HTML FTW!
- Oferece um modelo de programação conhecido. É apenas DOM/CSS/HTML.
- Funciona bem com outros novos recursos da plataforma da Web (DOM de sombra,
<template>
, propriedades personalizadas de CSS etc.) - Integração total com as ferramentas do desenvolvedor do navegador.
- Aproveite os recursos de acessibilidade atuais.