Com os elementos personalizados, você pode criar suas próprias tags HTML. Esta lista de verificação abrange as práticas recomendadas para ajudar você a criar elementos de alta qualidade.
Com os elementos personalizados, você pode estender o HTML e definir suas próprias tags. Eles são
incrivelmente eficiente, mas também são de baixo nível, o que significa que não são
sempre claro a melhor forma de implementar seu próprio elemento.
Para ajudar você a criar as melhores experiências possíveis, reunimos esta
lista de verificação. Ele detalha tudo que achamos que é preciso para ser um
um elemento personalizado bem-comportado.
Lista de verificação
Shadow DOM
Crie uma raiz paralela para encapsular estilos. |
Por quê? |
O encapsulamento de estilos na raiz paralela do elemento garante que ele funcione
independentemente de onde eles são usados. Isso é importante principalmente se um desenvolvedor
quer colocar seu elemento dentro da raiz paralela de outro elemento. Isso
se aplica até mesmo a elementos simples, como uma caixa de seleção ou um botão de opção. Pode ser
caso o único conteúdo da sua raiz paralela serão os estilos
por conta própria.
|
Exemplo |
O
<howto-checkbox> .
|
Crie sua raiz paralela no construtor.
|
Por quê? |
O construtor é quando você tem conhecimento exclusivo do seu elemento.
Este é um ótimo momento para configurar detalhes de implementação que você não quer que outras
com os elementos. Fazer esse trabalho em um retorno de chamada posterior, como o
connectedCallback , significa que você vai precisar se proteger contra
situações em que o elemento é desconectado e reanexado ao documento.
|
Exemplo |
O
<howto-checkbox> .
|
Coloque os filhos que o elemento criar na raiz paralela.
|
Por quê? |
Os filhos criados pelo seu elemento fazem parte da implementação e devem ser
privados. Sem a proteção de uma raiz paralela, o JavaScript externo pode
interferir inadvertidamente nessas crianças.
|
Exemplo |
O
<howto-tabs> .
|
Usar <slot> para projetar filhos do light DOM no shadow DOM
|
Por quê? |
Permite que os usuários do componente especifiquem conteúdo nele, já que os filhos do HTML tornam o componente mais combinável. Quando um navegador não aceita elementos personalizados, o conteúdo aninhado permanece disponível, visível e acessível.
|
Exemplo |
O
<howto-tabs> .
|
Defina um estilo de exibição para :host (por exemplo, block ,
inline-block , flex ), a menos que você prefira o padrão de
inline .
|
Por quê? |
Os elementos personalizados são display: inline por padrão. Portanto, definir os
width ou height não terão efeito. Isso muitas vezes
é uma surpresa para os desenvolvedores e pode causar problemas relacionados a
o layout da página. A menos que você prefira uma tela inline ,
precisa sempre definir um valor display padrão.
|
Exemplo |
O
<howto-checkbox> .
|
Adicione um estilo de exibição :host que respeite o atributo oculto.
|
Por quê? |
Um elemento personalizado com um estilo display padrão, por exemplo:
:host { display: block } , substituirá a menor especificidade
integrado
hidden .
Isso pode surpreender se você pretende definir o hidden
no seu elemento para renderizá-lo como display: none . Além disso,
para um estilo display padrão, adicione suporte para hidden
com :host([hidden]) { display: none } .
|
Exemplo |
O
<howto-checkbox> .
|
Atributos e propriedades
Não substitua os atributos globais definidos pelo autor.
|
Por quê? |
Atributos globais são aqueles que estão presentes em todos os elementos HTML. Algumas
como tabindex e role . Um elemento personalizado
pode definir o tabindex inicial como 0 para que seja teclado
focalizável. Mas você deve sempre verificar primeiro se o desenvolvedor que usa
o elemento definiu isso com outro valor. Se, por exemplo, ele tiver definido
tabindex a -1, é um sinal de que a pessoa não quer
elemento para ser interativo.
|
Exemplo |
O
<howto-checkbox> . Isso é explicado em mais detalhes
Não substitua o autor da página.
|
Sempre aceite dados primitivos (strings, números, booleanos) como atributos
ou propriedades.
|
Por quê? |
Os elementos personalizados, assim como os integrados, precisam ser configuráveis.
A configuração pode ser transmitida de maneira declarativa, por atributos ou
usando propriedades JavaScript. O ideal é que todos os atributos também estejam vinculados
uma propriedade correspondente.
|
Exemplo |
O
<howto-checkbox> .
|
Procure manter os atributos e as propriedades de dados primitivos em sincronia, refletindo a partir da
para atribuir a propriedade e vice-versa.
|
Por quê? |
Nunca se sabe como um usuário vai interagir com seu elemento. Eles poderiam
uma propriedade em JavaScript e vai ler esse valor
usando uma API, como getAttribute() . Se cada atributo tiver um
propriedade correspondente e ambas refletirem, facilitará para que
que os usuários trabalhem com seu elemento. Em outras palavras, chamar
setAttribute('foo', value) também precisa definir
foo e vice-versa. É claro que há exceções
essa regra. Não inclua propriedades de alta frequência, por exemplo,
currentTime em um player de vídeo. Use seu bom senso. Se
parece que um usuário vai interagir com uma propriedade ou um atributo, e
não é trabalhoso refletir isso, então faça isso.
|
Exemplo |
O
<howto-checkbox> . Isso é explicado em mais detalhes
Evite problemas de reentrada:
|
Procure aceitar apenas dados avançados (objetos, matrizes) como propriedades.
|
Por quê? |
De modo geral, não há exemplos de elementos HTML integrados que
aceitam dados avançados (objetos e matrizes JavaScript simples) por meio de
atributos. Os dados avançados são aceitos por chamadas de método ou
propriedades. Há algumas desvantagens óbvias em aceitar dados avançados
atributos: pode ser caro serializar um objeto grande em uma string e
quaisquer referências de objetos serão perdidas nesse processo de string. Para
exemplo, se você criar uma string para um objeto que tem uma referência a outro objeto,
ou talvez um nó DOM, essas referências serão perdidas.
|
Não refletem propriedades de dados avançados para atributos.
|
Por quê? |
Refletir propriedades de dados avançados em atributos é caro,
exigindo a serialização e desserialização dos mesmos objetos JavaScript. A menos que
você tem um caso de uso que só pode ser resolvido com esse recurso, provavelmente é
melhor evitá-lo.
|
Verifique se há propriedades definidas antes do elemento
atualizado.
|
Por quê? |
Um desenvolvedor que usa seu elemento pode tentar definir uma propriedade no elemento.
antes do carregamento da definição. Isso é especialmente verdadeiro se
desenvolvedor está usando um framework que lida com o carregamento de componentes, carimbando-os
à página e vincular as propriedades a um modelo.
|
Exemplo |
O
<howto-checkbox> . Mais explicados em
Torne as propriedades lentas.
|
Não aplique turmas automaticamente.
|
Por quê? |
Elementos que precisam expressar o estado precisam usar atributos. A
O atributo class geralmente é considerado de propriedade do
usando seu elemento. Além disso, gravar nele por conta própria pode
participar de aulas para desenvolvedores.
|
Eventos
Enviar eventos em resposta à atividade do componente interno.
|
Por quê? |
Seu componente pode ter propriedades que mudam em resposta à atividade que
que somente seu componente conhece, por exemplo, se um cronômetro ou animação
é concluída ou um recurso termina de carregar. É útil enviar eventos
em resposta a essas alterações, para notificar o host de que o estado do componente foi
diferente.
|
Não envie eventos em resposta à definição do host de uma propriedade (para baixo
fluxo de dados).
|
Por quê? |
Envio de um evento em resposta a uma configuração de host de uma propriedade é desnecessário
(o host sabe o estado atual porque acabou de defini-lo). Eventos de envio
em resposta à definição de um host, uma propriedade pode causar loops infinitos com
sistemas de vinculação.
|
Exemplo |
O
<howto-checkbox> .
|
Vídeos de explicação
Não substituir o autor da página
É possível que um desenvolvedor que use seu elemento queira modificar algumas
o estado inicial dela. Por exemplo, mudar a role
ARIA ou a capacidade de foco com
tabindex
. Verifique se esses e outros atributos globais foram definidos.
antes de aplicar seus próprios valores.
connectedCallback() {
if (!this.hasAttribute('role'))
this.setAttribute('role', 'checkbox');
if (!this.hasAttribute('tabindex'))
this.setAttribute('tabindex', 0);
Tornar as propriedades lentas
Um desenvolvedor pode tentar definir uma propriedade no seu elemento antes que o
foi carregada. Isso é especialmente verdadeiro se o desenvolvedor estiver usando um
que lida com o carregamento de componentes, inserindo-os na página e
vinculando as propriedades a um modelo.
No exemplo a seguir, o Angular vincula de forma declarativa o
isChecked
à propriedade checked
da caixa de seleção. Se a definição de
a caixa de seleção de instruções foi carregada lentamente. É possível que o Angular tente definir
a propriedade marcada antes do upgrade do elemento.
<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>
Um elemento personalizado deve lidar com esse cenário verificando se alguma propriedade tem
já foi definido na instância. O <howto-checkbox>
demonstra esse padrão usando um método chamado _upgradeProperty()
.
connectedCallback() {
...
this._upgradeProperty('checked');
}
_upgradeProperty(prop) {
if (this.hasOwnProperty(prop)) {
let value = this[prop];
delete this[prop];
this[prop] = value;
}
}
_upgradeProperty()
captura o valor da instância não atualizada e exclui
a propriedade para que ela não sombre o configurador de propriedades do próprio elemento personalizado.
Dessa forma, quando a definição do elemento for finalmente carregada, ela poderá
refletem o estado correto.
Evitar problemas de reentrada
É tentador usar attributeChangedCallback()
para refletir o estado de uma
propriedade subjacente, por exemplo:
// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'checked')
this.checked = newValue;
}
Mas isso pode criar um loop infinito se o setter da propriedade também refletir
o 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');
}
Uma alternativa é permitir que o setter da propriedade reflita o atributo, e
faça com que o getter determine seu valor com base no atributo.
set checked(value) {
const isChecked = Boolean(value);
if (isChecked)
this.setAttribute('checked', '');
else
this.removeAttribute('checked');
}
get checked() {
return this.hasAttribute('checked');
}
Nesse exemplo, a adição ou remoção do atributo também definirá a propriedade.
Por fim, o attributeChangedCallback()
pode ser usado para processar efeitos colaterais
como aplicar estados 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;
...
}
}