Elementos personalizados v1 - Componentes da Web reutilizáveis

Os elementos personalizados permitem que os desenvolvedores Web definam novas tags HTML, estendam as existentes e criem componentes da Web reutilizáveis.

Com os Elementos personalizados, os desenvolvedores Web podem criar novas tags HTML, melhorar as tags HTML existentes ou ampliar os componentes que outros desenvolvedores têm criados. A API é a base do Web e componentes de solução. Ele traz um baseada em padrões de criar componentes reutilizáveis usando apenas JavaScript/HTML/CSS básico. O resultado é menos código, código modular e mais reutilização em nossos apps.

Introdução

O navegador nos oferece uma excelente ferramenta para estruturar aplicativos da web. Está chamada HTML. Talvez você já tenha ouvido falar dele. É declarativa, portátil suportado e fácil de trabalhar. Ótimo como o HTML seja, seu vocabulário e e a extensibilidade são limitadas. A vida do HTML padrão sempre não teve uma maneira de associar automaticamente o comportamento do JS à sua marcação... até agora.

Os elementos personalizados são a resposta para a modernização do HTML, completando o que falta peças e agrupamento de estrutura com comportamento. Se o HTML não fornecer o solução para um problema, podemos criar um elemento personalizado que faça isso. Personalizado de recursos ensinam novos truques ao navegador, preservando os benefícios do HTML.

Definição de um novo elemento

Para definir um novo elemento HTML, precisamos do poder do JavaScript!

O objeto global customElements é usado para definir um elemento personalizado e ensinar o navegador sobre uma nova tag. Chamar customElements.define() com o nome da tag. que você quer criar e um class JavaScript que estenda a base HTMLElement.

Exemplo: definir 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 usar um elemento personalizado não é diferente usando um <div> ou qualquer outro elemento. As instâncias podem ser declaradas na página, criados dinamicamente em JavaScript, listeners de eventos podem ser anexados etc. O Keep para mais exemplos.

Definir a API JavaScript de um elemento

A funcionalidade de um elemento personalizado é definida usando um ES2015 class que estende HTMLElement. Estender HTMLElement garante que o elemento personalizado herda toda a API DOM e significa qualquer propriedade/método adicionado ao se tornam parte da interface DOM do elemento. Basicamente, use a classe para criar uma API JavaScript pública para sua tag.

Exemplo: definir a 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, estamos criando uma gaveta que tem uma propriedade open, disabled. e um método toggleDrawer(). Ele também reflete as propriedades como HTML atributos.

Um recurso interessante de elementos personalizados é que this dentro de uma definição de classe refere-se ao próprio elemento DOM, ou seja, a instância da classe. Em nossa Por exemplo, this refere-se a <app-drawer>. É assim (nerg) que o elemento pode anexar um listener click a si mesmo. E você não se limita a listeners de eventos. Toda a API DOM está disponível dentro do código do elemento. Use this para acessar propriedades do elemento, inspecionar seus filhos (this.children), consultar nós (this.querySelectorAll('.items')) etc.

Regras para a criação de elementos personalizados

  1. O nome de um elemento personalizado precisa conter um traço (-). Então, <x-tags>, <my-element> e <my-awesome-app> são nomes válidos, enquanto <tabs> e <foo_bar> não. Esse requisito é para que o analisador HTML possa distinguir elementos personalizados de elementos regulares. Ele também garante o encaminhamento quando novas tags são adicionadas ao HTML.
  2. Você não pode registrar a mesma tag mais de uma vez. A tentativa de fazer isso gerar uma DOMException. Depois de informar o navegador sobre uma nova tag, reimplantá-lo. Não é possível reverter.
  3. Os elementos personalizados não podem fechar automaticamente porque o HTML permite apenas alguns elementos fecham automaticamente. Sempre escreva uma tag de fechamento (<app-drawer></app-drawer>).

Reações do elemento personalizado

Um elemento personalizado pode definir hooks de ciclo de vida especiais para executar o código durante em momentos interessantes da sua existência. Eles são chamados de elementos personalizados reações.

Nome Chamado quando
constructor Uma instância do elemento é criados ou atualizados. Útil para a inicialização estado, configuração de listeners de eventos ou Como criar um shadow DOM. Consulte a especificações para saber o que é possível fazer no constructor.
connectedCallback Chamado sempre que o é inserido no DOM. Útil para executar códigos de configuração, como a busca de recursos ou renderização. Em geral, você deve tentar atrasar o trabalho até este momento.
disconnectedCallback Chamado sempre que o elemento é removido do DOM. Útil para e executar códigos de limpeza.
attributeChangedCallback(attrName, oldVal, newVal) Chamado quando um atributo observado foi adicionados, removidos, atualizados ou substituídos. Também chamado para valores iniciais quando um elemento é criado pelo analisador ou atualizado. Observação: somente atributos listados na propriedade observedAttributes para receber esse callback.
adoptedCallback A 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 ligar para el.setAttribute() no seu elemento, o navegador chamará attributeChangedCallback() imediatamente. Da mesma forma, você receberá um disconnectedCallback() logo após o elemento ser removido do DOM (por exemplo, o usuário chama el.remove()).

Exemplo:adicionar reações de elemento personalizado a <app-drawer>:

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

  connectedCallback() {
    // ...
  }

  disconnectedCallback() {
    // ...
  }

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

Defina as reações se/quando fizer sentido. Se o elemento for complexo o suficiente e abrir uma conexão com o IndexedDB em connectedCallback(), faça o que for necessário trabalho de limpeza em disconnectedCallback(). Mas tenha cuidado. Você não pode confiar nos seus sendo removido do DOM em todas as circunstâncias. Por exemplo: disconnectedCallback() nunca será chamado se o usuário fechar a guia.

Propriedades e atributos

Refletir propriedades para atributos

É comum que as propriedades HTML reflitam seu valor de volta para o DOM como uma atributo HTML. Por exemplo, quando os valores de hidden ou id são alterados JavaScript:

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

os valores são aplicados ao DOM ativo como atributos:

<div id="my-id" hidden>

Isso é chamado de "refletir as propriedades para atributos". Quase todas as propriedades do HTML fazem isso. Por quê? Os atributos também são úteis para configurar um elemento de maneira declarativa e determinadas APIs, como acessibilidade e CSS os seletores dependem de atributos para funcionar.

Refletir uma propriedade é útil sempre que você quiser manter o DOM do elemento. em sincronia com o estado do JavaScript. Um motivo pelo qual você pode querer refletem uma propriedade, então o estilo definido pelo usuário é aplicado quando o estado do JS muda.

Lembre-se da nossa <app-drawer>. Um consumidor desse componente pode querer esmaecê-lo e/ou impedir a interação do usuário quando ela estiver desativada:

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 do 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 em atributos

Os atributos HTML são uma maneira conveniente para os usuários declararem o estado inicial:

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

Os elementos podem reagir a mudanças de atributos definindo uma attributeChangedCallback: O navegador chamará esse método para cada alteração aos 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 atributos adicionais na <app-drawer> quando um O atributo disabled foi alterado. Embora não façamos isso aqui, é possível também use o attributeChangedCallback para manter uma propriedade JS sincronizada com o Atributo.

Upgrades de elementos

HTML aprimorado progressivamente

Já aprendemos que elementos personalizados são definidos chamando customElements.define(): Mas isso não significa que você precisa definir + registrar uma elemento personalizado de uma só vez.

Os elementos personalizados podem ser usados antes do registro da definição.

O aprimoramento progressivo é um recurso dos elementos personalizados. Em outras palavras, você pode 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 possíveis elementos personalizados de forma diferente graças a uma palavra-chave desconhecida tags. O processo de chamar define() e aprimorar um com uma definição de classe é chamado de "upgrade de elementos".

Para saber quando um nome de tag é definido, use window.customElements.whenDefined(): Ela retorna uma promessa que é resolvida quando é 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

Elementos personalizados podem gerenciar seu próprio conteúdo usando as APIs do DOM 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>";
  }
  // ...
});

Declarar essa tag gera:

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

// TODO: DevSite - Exemplo de código removido porque usava manipuladores de eventos in-line

Como criar um elemento que usa o Shadow DOM

O Shadow DOM permite que um elemento tenha, renderize e estilize um bloco de um DOM separado do restante da página. Você pode até esconder aplicativo inteiro com 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 da 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 - Exemplo de código removido porque usava manipuladores de eventos in-line

Como criar elementos usando uma <template>

Para quem não conhece, o <template> elemento permite declarar fragmentos do DOM que são analisados, ficam inertes no carregamento da página e que podem ser ativados no momento da execução. É outro primitivo de API na Web. família de componentes. Os modelos são um marcador de posição ideal para declarar os estrutural de um elemento personalizado.

Exemplo:registrar um elemento com conteúdo do Shadow DOM criado usando 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 &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>

Essas poucas linhas de código fazem um grande sucesso. Vamos entender os principais pontos em:

  1. Estamos definindo um novo elemento em HTML: <x-foo-from-template>
  2. O Shadow DOM do elemento é criado usando uma <template>
  3. O DOM do elemento é local em relação ao elemento graças ao Shadow DOM
  4. O CSS interno do elemento tem o escopo definido para o elemento graças ao Shadow DOM

Estou no Shadow DOM. Minha marcação foi estampada de um <template>.

// TODO: DevSite - Exemplo de código removido porque usava manipuladores de eventos in-line

Definir o estilo de um elemento personalizado

Mesmo que o elemento defina o próprio estilo usando o Shadow DOM, os usuários podem estilizar seu 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>

Talvez você esteja se perguntando como funciona a especificidade de CSS se o elemento tem estilos definidos no Shadow DOM. Em termos de especificidade, os estilos de usuário vencem. Eles vão sempre substituir o estilo definido pelo elemento. Consulte a seção sobre Como criar um elemento que usa o Shadow DOM.

Pré-estimar elementos não registrados

Antes do upgrade de um elemento, ele pode ser segmentado no CSS usando a pseudoclasse :defined. Isso é útil para aplicar um estilo predefinido a um componente. Para por exemplo, você pode querer evitar o layout ou outro FOUC visual ocultando elementos componentes e esmaecendo-os quando eles são definidos.

Exemplo: oculte <app-drawer> antes de definir:

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> for definido, o seletor (app-drawer:not(:defined)) não corresponde mais.

Estender elementos

A API Custom Elements é útil para criar elementos HTML, mas também útil para estender outros elementos personalizados ou até mesmo o HTML integrado do navegador.

Estender um elemento personalizado

A extensão de outro elemento personalizado é feita estendendo sua definição de classe.

Exemplo: crie <fancy-app-drawer> que estenda <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);

Ampliar elementos HTML nativos

Digamos que você queira criar um <button> mais sofisticado. Em vez de replicar o comportamento e funcionalidade do <button>, uma opção melhor é progressivamente aprimorar o elemento existente usando elementos personalizados.

Um elemento integrado personalizado é um elemento personalizado que estende um dos tags HTML integradas do navegador. O principal benefício de ampliar é ganhar todos os seus recursos (propriedades DOM, métodos, acessibilidade). Não há melhor maneira de criar uma Web Progressiva do que aprimorar progressivamente o HTML existente elementos.

Para estender um elemento, é preciso criar uma definição de classe que herde 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 - extensão 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'});

A chamada para define() muda um pouco ao estender um objeto . O terceiro parâmetro obrigatório informa ao navegador qual tag está sendo que se estendem. Isso é necessário porque muitas tags HTML compartilham o mesmo DOM interface gráfica do usuário. <section>, <address> e <em> (entre outros) compartilham HTMLElement; <q> e <blockquote> compartilham HTMLQuoteElement; etc... Especificar {extends: 'blockquote'} permite que o navegador saiba que você está criando um <blockquote> otimizado em vez de um <q>. Consulte o HTML especificação para obter a lista completa de interfaces DOM do HTML.

Os consumidores de um elemento integrado personalizado podem usá-lo de várias maneiras. Eles podem declare-o 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;

Veja outro exemplo que estende <img>.

Exemplo - extensão 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'});

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 é leniente e flexível para trabalhar. Por exemplo, declare <randomtagthatdoesntexist> em uma página e o navegador está perfeitamente satisfeito de aceitá-lo. Por que as tags não padrão funcionam? A resposta é HTML especificação que permite. Elementos que não são definidos pela especificação são analisados como HTMLUnknownElement:

O mesmo não é verdade para elementos personalizados. Os possíveis elementos personalizados são analisados como um HTMLElement se forem criados com um nome válido (incluindo um "-"). Você pode verificar isso em um navegador compatível com elementos personalizados. Acione o console: Pressione Ctrl + Shift + J (ou Cmd + Opt + J no Mac) e cole no 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 dados os elementos.

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. Retornará 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 será resolvida quando o elemento personalizado for definido. Se o elemento já estiver definido, resolva imediatamente. Rejeita a solicitação 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 do navegador

Se você acompanhou os componentes da Web nos últimos dois anos, saber que o Chrome 36+ implementou uma versão da API Custom Elements que usa document.registerElement() em vez de customElements.define(). Agora é considerada uma versão descontinuada do padrão, chamada v0. customElements.define() é a novidade e quais são os fornecedores de navegadores começar a implementar. Ele é chamado de Custom Elements v1.

Se você tiver interesse na especificação v0 antiga, confira a documentação do html5rocks artigo.

Suporte ao navegador

Chrome 54 (status) Safari 10.1 (status) e O Firefox 63 (status) tem Elementos personalizados v1. O Edge começou no desenvolvimento de software.

Para detectar elementos personalizados, verifique a existência de window.customElements:

const supportsCustomElementsV1 = 'customElements' in window;

Polyfill

Até que o suporte a navegadores esteja amplamente disponível, polyfill autônomo (em inglês) disponível para elementos personalizados v1. No entanto, recomendamos usar o pacote webcomponents.js carregador para carregar de maneira ideal os polyfills dos componentes da Web. O carregador usa a detecção de recursos para carregar de forma assíncrona somente os Pollyfills necessários exigidos 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 nos oferecem uma nova ferramenta para definir novas tags HTML no navegador e criar componentes reutilizáveis. Combine-os com a outra plataforma nova primitivos, como o Shadow DOM e o <template>, e começar a perceber os imagem dos Web Components:

  • Compatível com todos os navegadores (padrão da Web) para criar e ampliar componentes reutilizáveis.
  • Não exige biblioteca ou framework para começar. JS/HTML comum! UF!
  • fornece um modelo de programação familiar; É apenas DOM/CSS/HTML.
  • Funciona bem com outros novos recursos da plataforma da Web (shadow DOM, <template>, CSS) propriedades personalizadas etc.)
  • Estreitamente integrado com o DevTools do navegador.
  • Aproveite os recursos de acessibilidade atuais.