[Leitura recomendada] As outras 19 partes do ciclo Hoje, na tradução de 17 partes dos materiais dedicados aos recursos de tudo que está de alguma forma conectado ao JavaScript, falaremos sobre componentes da Web e vários padrões que visam trabalhar com eles. Atenção especial será dada à tecnologia Shadow DOM.

Revisão
Os componentes da Web são uma família de APIs projetadas para descrever novos elementos DOM adequados para reutilização. A funcionalidade desses elementos é separada do restante do código; eles podem ser usados em aplicativos da web de nosso próprio design.
Existem quatro tecnologias relacionadas aos componentes da web:
- DOM da sombra (DOM da sombra)
- Modelos HTML (Modelos HTML)
- Elementos personalizados
- Importações HTML (Importação HTML)
Neste artigo, falaremos sobre a tecnologia Shadow DOM, projetada para criar aplicativos baseados em componentes. Ele oferece maneiras de resolver problemas comuns de desenvolvimento da web que você já pode ter encontrado:
- Isolamento do DOM: o componente possui uma árvore DOM isolada (isso significa que o comando
document.querySelector()
não permitirá acesso ao nó no DOM sombra do componente). Além disso, simplifica o sistema seletor de CSS em aplicativos da Web, uma vez que os componentes do DOM são isolados, o que permite ao desenvolvedor usar os mesmos identificadores universais e nomes de classes em componentes diferentes sem se preocupar com possíveis conflitos de nomes. - Isolamento CSS: as regras CSS descritas dentro do DOM sombra são limitadas a ele. Esses estilos não deixam o elemento, eles não se misturam com outros estilos de página.
- Composição: desenvolvendo uma API declarativa para componentes baseados em marcação.
Tecnologia Shadow DOM
Pressupõe que você já esteja familiarizado com o conceito do DOM e as APIs associadas. Caso contrário, você pode ler
este material.
O DOM Shadow é basicamente o mesmo que um DOM normal, mas com duas diferenças:
- A primeira é como o Shadow DOM é criado e usado, em particular, é sobre o relacionamento do Shadow DOM com o restante da página.
- O segundo é o comportamento do DOM da sombra em relação à página.
Ao trabalhar com o DOM, são criados nós DOM que se associam, como filhos, a outros elementos da página. No caso da tecnologia Shadow DOM, é criada uma árvore DOM isolada que une o elemento, mas é separada de seus elementos filhos normais.
Essa subárvore isolada é chamada de árvore das sombras. O elemento ao qual essa árvore está anexada é chamado host de sombra. Tudo o que é adicionado à subárvore DOM da sombra acaba sendo local para o elemento ao qual está anexado, incluindo os estilos descritos usando as tags
<style>
. É assim que o isolamento CSS é fornecido por meio da tecnologia Shadow DOM.
Criando um DOM Shadow
A raiz da sombra é uma parte do documento anexada ao elemento host. Um elemento adquire um DOM de sombra quando um elemento raiz de sombra é anexado a ele. Para criar um DOM de sombra para um determinado elemento, você precisa usar um comando do formulário
element.attachShadow()
:
var header = document.createElement('header'); var shadowRoot = header.attachShadow({mode: 'open'}); shadowRoot.appendChild(document.createElement('<p> Shadow DOM </p>');
Deve-se notar que, na
especificação Shadow DOM, há uma lista de elementos aos quais as subárvores DOM shadow não podem ser conectadas.
Composição no Shadow DOM
A composição é um dos recursos mais importantes do Shadow DOM, é uma maneira de criar aplicativos da web, usados no processo de escrever código HTML. Durante esse processo, o programador combina os vários blocos de construção (elementos) que compõem a página, aninhando-os, se necessário, um no outro. Por exemplo, são elementos como
<div>
,
<header>
,
<form>
e outros usados para criar interfaces de aplicativos da web, incluindo aqueles que atuam como contêineres para outros elementos.
A composição determina os recursos de elementos, como
<select>
,
<form>
,
<video>
, para incluir outros elementos HTML como filhos e a capacidade de organizar o comportamento especial de tais estruturas que consistem em diferentes elementos.
Por exemplo, o elemento
<select>
possui meios para renderizar elementos
<option>
na forma de uma lista suspensa com o conteúdo predeterminado dos elementos dessa lista.
Considere alguns dos recursos do Shadow DOM usados na composição de elementos.
Dom luz
Light DOM é a marcação criada pelo usuário do seu componente. Este DOM está fora do DOM de sombra do componente e é filho do componente. Imagine que você criou um componente personalizado chamado
<better-button>
que estende os recursos do elemento HTML
<button>
padrão, e o usuário precisa adicionar uma imagem e algum texto a esse novo elemento. Aqui está o que parece:
<extended-button> <img align="center" src="boot.png" slot="image"> <span>Launch</span> </extended-button>
O elemento
<extended-button>
é um componente personalizado descrito pelo programador por conta própria, e o código HTML dentro deste componente é seu Light DOM - o que o usuário deste componente adicionou a ele.
O DOM de sombra neste exemplo é o componente
<extended-button>
. Este é um modelo de objeto local de um componente que descreve sua estrutura interna, isolada do mundo externo do CSS, e encapsula os detalhes de implementação do componente.
Dom achatado
A árvore DOM achatada representa como o navegador exibe o componente na tela, combinando o Light DOM e o Shadow DOM. É uma árvore DOM que pode ser vista nas ferramentas do desenvolvedor e é exibida na página. Pode ser algo como isto:
<extended-button> #shadow-root <style>…</style> <slot name="image"> <img align="center" src="boot.png" slot="image"> </slot> <span id="container"> <slot> <span>Launch</span> </slot> </span> </extended-button>
Padrões
Se você precisar usar constantemente as mesmas estruturas na marcação HTML das páginas da Web, será útil usar um determinado modelo em vez de escrever o mesmo código repetidamente. Isso já era possível antes, mas agora tudo foi bastante simplificado graças à aparência da
<template>
HTML
<template>
, que possui excelente suporte para navegadores modernos. Este elemento e seu conteúdo não são exibidos no DOM, mas você pode trabalhar com ele no JavaScript. Considere um exemplo simples:
<template id="my-paragraph"> <p> Paragraph content. </p> </template>
Se você incluir esse design na marcação HTML da página, o conteúdo da tag
<p>
descrita por ela não aparecerá na tela até que seja explicitamente anexado ao DOM do documento. Por exemplo, pode ser assim:
var template = document.getElementById('my-paragraph'); var templateContent = template.content; document.body.appendChild(templateContent);
Existem outros meios para obter o mesmo efeito, mas, como já mencionado, os modelos são uma ferramenta padrão muito conveniente que possui um bom suporte ao navegador.
Suporte de navegador HTML para navegadores modernosOs modelos são úteis por si só, mas seus recursos são totalmente divulgados quando usados com elementos personalizados. elementos personalizados - este é um assunto para um artigo separado, mas agora, para a compreensão do que está acontecendo, é suficiente para levar em conta o fato de que o navegador API
customElement
permite que o programador para descrever suas próprias HTML-tags e definir como os elementos são criados com estas tags será exibido na tela.
Defina um componente da web que usa nosso modelo como conteúdo para seu DOM sombra. Chame esse novo elemento
<my-paragraph>
:
customElements.define('my-paragraph', class extends HTMLElement { constructor() { super(); let template = document.getElementById('my-paragraph'); let templateContent = template.content; const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true)); } });
O mais importante a se prestar atenção é que anexamos um clone do conteúdo do modelo criado usando o método
Node.cloneNode () à raiz da sombra.
Como anexamos o conteúdo do modelo ao DOM da sombra, podemos incluir algumas informações de estilo no modelo no elemento
<style> , que serão encapsuladas no elemento user. Todo esse esquema não funcionará conforme o esperado se você trabalhar com o DOM normal, em vez do DOM da sombra.
Por exemplo, um modelo pode ser modificado da seguinte maneira, incluindo informações de estilo:
<template id="my-paragraph"> <style> p { color: white; background-color: #666; padding: 5px; } </style> <p>Paragraph content. </p> </template>
Agora, o elemento de usuário descrito por nós pode ser usado em páginas da Web comuns da seguinte maneira:
<my-paragraph></my-paragraph>
Slots
Os modelos HTML têm várias desvantagens, a principal é que os modelos contêm marcação estática, o que não permite, por exemplo, exibir o conteúdo de determinadas variáveis com sua ajuda para trabalhar com elas da mesma maneira que trabalham com HTML padrão padrões. É aqui que a tag
<slot>
entra.
Os slots podem ser percebidos como espaços reservados que permitem incluir seu próprio código HTML no modelo. Isso permite criar modelos HTML universais e torná-los personalizáveis adicionando slots a eles.
Veja como o modelo acima será exibido usando a
<slot>
:
<template id="my-paragraph"> <p> <slot name="my-text">Default text</slot> </p> </template>
Se o conteúdo do slot não for especificado quando o elemento estiver incluído na marcação, ou se o navegador não suportar o trabalho com slots, o elemento
<my-paragraph>
incluirá apenas o conteúdo
Default text
.
Para definir o conteúdo do slot, é necessário incluir o código HTML com o atributo
slot
no elemento
<my-paragraph>
, cujo valor é equivalente ao nome do slot no qual você deseja colocar esse código.
Como antes, pode haver qualquer coisa. Por exemplo:
<my-paragraph> <span slot="my-text">Let's have some different text!</span> </my-paragraph>
Os elementos que podem ser colocados nos slots são chamados de elementos
Slotable .
Observe que no exemplo anterior, adicionamos o elemento
<span>
ao slot, que é o chamado elemento com fenda. Ele tem o atributo de
slot
, que é definido
my-text
, que é - o mesmo valor, que é usado no atributo
name
ranhura conforme descrito no modelo.
Após o processamento da marcação acima, o navegador criará a seguinte árvore DOM achatada:
<my-paragraph> #shadow-root <p> <slot name="my-text"> <span slot="my-text">Let's have some different text!</span> </slot> </p> </my-paragraph>
Preste atenção ao elemento
#shadow-root
. Este é apenas um indicador da existência do DOM da sombra.
Estilização
Os componentes que usam a tecnologia Shadow DOM podem ser estilizados de maneira comum, podem definir seus próprios estilos ou fornecer ganchos na forma de
propriedades CSS personalizadas que permitem que os usuários de componentes substituam os estilos padrão.
Described Estilos descritos nos componentes
O isolamento de CSS é um dos recursos mais notáveis da tecnologia Shadow DOM. Ou seja, estamos falando sobre o seguinte:
- Os seletores de CSS da página na qual o componente correspondente é colocado não afetam o que ele contém.
- Os estilos descritos no componente não afetam a página. Eles são isolados no elemento host.
Os seletores de CSS usados no DOM sombra aplicam-se localmente ao conteúdo do componente. Na prática, isso significa a capacidade de reutilizar os mesmos identificadores e nomes de classes em diferentes componentes e não é necessário se preocupar com conflitos de nomes. Seletores CSS simples também significam melhor desempenho para as soluções em que são usados.
Dê uma olhada no elemento
#shadow-root
, que define alguns estilos:
#shadow-root <style> #container { background: white; } #container-items { display: inline-flex; } </style> <div id="container"></div> <div id="container-items"></div>
Todos os estilos acima são locais para
#shadow-root
.
Além disso, você pode usar a tag
<link>
para incluir folhas de estilo externas em
#shadow-root
. Tais estilos também serão locais.
SePseudoclasse: host
A pseudo
:host
permite acessar um elemento que contém uma árvore DOM sombra e estilizar esse elemento:
<style> :host { display: block; } </style>
Usando a pseudo
:host
, lembre-se de que as regras da página pai têm prioridade mais alta do que as especificadas no elemento usando essa pseudo classe. Isso permite que os usuários substituam os estilos de componentes do host definidos nele de fora. Além disso, a pseudo-
:host
funciona apenas no contexto do elemento raiz da sombra; você não pode usá-lo fora da árvore DOM da sombra.
A forma funcional da pseudo-classe:
:host(<selector>)
permite acessar o elemento host se ele corresponder ao elemento
<selector>
especificado. Essa é uma ótima maneira de permitir que os componentes encapsulem o comportamento que responde às ações do usuário ou alterações no estado de um componente e permite que você estilize nós internos com base no componente host:
<style> :host { opacity: 0.4; } :host(:hover) { opacity: 1; } :host([disabled]) { background: grey; pointer-events: none; opacity: 0.4; } :host(.pink) > #tabs { color: pink; } </style>
▍ Temas e elementos com uma pseudo-classe: contexto do host (<seletor>)
A pseudo-classe
:host-context(<selector>)
) correspondente ao elemento de host, se ele ou qualquer de seus antepassados coincidir com o elemento de dado
<selector>
.
Um caso de uso comum para esse recurso é estilizar elementos com temas. Por exemplo, os temas são frequentemente usados atribuindo a classe apropriada às tags
<html>
ou
<body>
:
<body class="lightheme"> <custom-container> … </custom-container> </body>
A pseudo-
:host-context(.lightheme)
será aplicada a
<fancy-tabs>
se esse elemento for descendente de
.lightteme
:
:host-context(.lightheme) { color: black; background: white; }
A construção
:host-context()
pode ser útil para aplicar temas, mas para esse propósito, é melhor usar ganchos usando
propriedades CSS personalizadas .
▍ Modelando o elemento host do componente de fora
O elemento host do componente pode ser estilizado externamente usando o nome de sua tag como um seletor:
custom-container { color: red; }
Os estilos externos têm precedência sobre os estilos definidos no DOM da sombra.
Suponha que um usuário crie o seguinte seletor:
custom-container { width: 500px; }
Ele substituirá a regra definida no próprio componente:
:host { width: 300px; }
Usando essa abordagem, você pode estilizar apenas o componente em si. Como estilizar a estrutura interna de um componente? Propriedades CSS personalizadas são usadas para esse fim.
Criando ganchos de estilo usando propriedades CSS personalizadas
Os usuários podem personalizar os estilos das estruturas internas dos componentes se o autor do componente fornecer ganchos de estilo usando
propriedades CSS personalizadas .
Essa abordagem é baseada em um mecanismo semelhante ao usado ao trabalhar com tags
<slot>
, mas, nesse caso, se aplica a estilos.
Considere um exemplo:
<style> custom-container { margin-bottom: 60px; - custom-container-bg: black; } </style> <custom-container background>…</custom-container>
Aqui está o que está dentro da árvore DOM da sombra:
:host([background]) { background: var( - custom-container-bg, #CECECE); border-radius: 10px; padding: 10px; }
Nesse caso, o componente usa preto como cor de fundo, pois foi o usuário que o especificou. Caso contrário, a cor de fundo será
#CECECE
.
Como autor do componente, você é responsável por informar aos usuários quais propriedades CSS específicas eles podem usar. Considere esta parte da interface aberta do seu componente.
API JavaScript para trabalhar com slots
A API Shadow DOM fornece a capacidade de trabalhar com slots.
▍Evento de troca de slot
O evento
slotchange
gerado quando os nós colocados no slot são alterados. Por exemplo, se um usuário adicionar ou remover nós filhos no Light DOM:
var slot = this.shadowRoot.querySelector('#some_slot'); slot.addEventListener('slotchange', function(e) { console.log('Light DOM change'); });
Para rastrear outros tipos de alterações no Light DOM, você pode usar
MutationObserver
no construtor do elemento. Leia mais sobre isso
aqui .
▍ Método assignNodes ()
O método
assignedNodes()
pode ser útil se você precisar saber quais elementos estão associados ao slot. Chamar o método
slot.assignedNodes()
permite descobrir exatamente quais elementos são exibidos pelo slot. O uso da opção
{flatten: true}
permite obter o conteúdo padrão do slot (exibido se nenhum nó estiver anexado a ele).
Considere um exemplo:
<slot name='slot1'><p>Default content</p></slot>
Imagine que esse slot esteja localizado no componente
<my-container>
.
Vejamos os vários usos do componente e que será emitido chamada de método
assignedNodes()
.
No primeiro caso, adicionamos nosso próprio conteúdo ao espaço:
<my-container> <span slot="slot1"> container text </span> </my-container>
Nesse caso, a chamada
assignedNodes()
retornará
[ container text ]
. Observe que esse valor é uma matriz de nós.
No segundo caso, não preenchemos o espaço com nosso próprio conteúdo:
<my-container> </my-container>
A chamada
assignedNodes()
retornará uma matriz vazia -
[]
.
Se, no entanto, você passar o parâmetro
{flatten: true}
para esse método, chamá-lo para o mesmo elemento retornará seu conteúdo padrão:
[ Default content ]
[ Default content ]
[ Default content ]
Além disso, para acessar um elemento dentro do slot, você pode chamar
assignedNodes()
para informar a qual slot de componente seu elemento está atribuído.
Modelo de evento
Vamos falar sobre o que acontece quando um evento que aparece na sombra da árvore DOM aparece. O objetivo do evento é definido levando em consideração o encapsulamento suportado pela tecnologia Shadow DOM. Quando um evento é redirecionado, parece que ele vem do próprio componente, e não de seu elemento interno, localizado na árvore DOM da sombra e que faz parte desse componente.
Aqui está uma lista de eventos que são transmitidos da árvore de sombra DOM (esse comportamento não é característico de alguns eventos):
- Eventos foco (eventos de foco Ler):
blur
, focus
, focusin
, focusout
. - Eventos do mouse s:
click
, dblclick
, mousedown
, mouseenter
, mousemove
e outros. - Eventos da
wheel
: wheel
. - Eventos de entrada:
beforeinput
input
, input
. - Eventos do teclado:
keydown
, keyup
. - Eventos de
compositionstart
: compositionstart
, compositionupdate
, compositionend
. - Arrastar eventos:
drag
, drag
, drag
, drop
e assim por diante.
Eventos personalizados
Os eventos do usuário por padrão não deixam a árvore de sombra do DOM. Se você deseja acionar um evento e deseja que ele saia do Shadow DOM, é necessário fornecer os parâmetros
bubbles: true
e
composed: true
. É assim que a chamada de um evento como esse se parece:
var container = this.shadowRoot.querySelector('#container'); container.dispatchEvent(new Event('containerchanged', {bubbles: true, composed: true}));
Suporte para navegadores Shadow DOM
Para descobrir se o navegador suporta a tecnologia Shadow DOM, você pode verificar a presença de
attachShadow
:
const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;
Aqui estão as informações sobre como vários navegadores suportam essa tecnologia.
Suporte à tecnologia Shadow DOM nos navegadoresSumário
A árvore DOM de sombra não se comporta como uma árvore DOM normal. Em particular, de acordo com o autor deste material, na biblioteca
SessionStack isso é expresso na complicação do procedimento para rastrear alterações do DOM, informações sobre as quais são necessárias para reproduzir o que aconteceu com a página. Ou seja,
MutationObserver
usado para rastrear alterações. Nesse caso, a árvore de sombra do DOM não gera o evento
MutationObserver
no escopo global, o que leva à necessidade de usar abordagens especiais para trabalhar com componentes que usam o DOM da sombra.
, - Shadow DOM, , , , .
Caros leitores! -, Shadow DOM?
