Introdução
O Web Components é um conjunto de padrões modernos que:
- Possibilitar a criação de widgets
- ... que podem ser reutilizados de maneira confiável
- E que não corromper as páginas se a próxima versão do componente altera os detalhes de implementação internos.
Isso significa que você precisa decidir quando usar HTML/JavaScript e quando usar os Web Components? Não! HTML e JavaScript podem tornar elementos visuais interativos. Os widgets são recursos visuais interativos. Ela faz sentido aproveitar suas habilidades HTML e JavaScript ao desenvolver um widget. Os padrões de componentes da Web são projetados para ajudar fazer isso.
Mas há um problema fundamental que torna os widgets criados a partir HTML e JavaScript difíceis de usar: a árvore do DOM em um widget não é encapsulados do restante da página. Essa falta de encapsulamento significa que a folha de estilo do documento pode ser aplicada acidentalmente a partes dentro do widget. o JavaScript pode modificar acidentalmente partes dentro do widget. seus IDs podem se sobrepor aos IDs dentro do widget. e assim por diante.
O Web Components é composto por três partes:
O Shadow DOM resolve o problema de encapsulamento da árvore do DOM. A quatro partes dos Web Components são projetadas para funcionar juntas, mas você pode escolher quais partes dos Web Components usar. Isso mostra como usar o Shadow DOM.
Olá, Mundo das Sombras
Com o Shadow DOM, os elementos podem receber um novo tipo de nó associado para resolvê-los com rapidez. Esse novo tipo de nó é chamado de raiz paralela. Um elemento que tem uma raiz paralela associada a ele é chamado de sombra host. O conteúdo de um host sombra não é renderizado. o conteúdo de a raiz paralela será renderizada.
Por exemplo, se você tivesse uma marcação como esta:
<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>
então, em vez de
<button id="ex1a">Hello, world!</button>
<script>
function remove(selector) {
Array.prototype.forEach.call(
document.querySelectorAll(selector),
function (node) { node.parentNode.removeChild(node); });
}
if (!HTMLElement.prototype.createShadowRoot) {
remove('#ex1a');
document.write('<img src="SS1.png" alt="Screenshot of a button with \'Hello, world!\' on it.">');
}
</script>
como sua página está
<button id="ex1b">Hello, world!</button>
<script>
(function () {
if (!HTMLElement.prototype.createShadowRoot) {
remove('#ex1b');
document.write('<img src="SS2.png" alt="Screenshot of a button with \'Hello, shadow world!\' in Japanese on it.">');
return;
}
var host = document.querySelector('#ex1b');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
})();
</script>
Além disso, se o JavaScript na página perguntar qual é o
textContent
não,
"ここんろちちん影の世界!", mas "Olá, mundo!" porque a subárvore do DOM
na raiz paralela é encapsulado.
Como separar o conteúdo da apresentação
Agora veremos como usar o Shadow DOM para separar o conteúdo do apresentação. Digamos que temos esta tag de nome:
<style>
.ex2a.outer {
border: 2px solid brown;
border-radius: 1em;
background: red;
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
}
.ex2a .boilerplate {
color: white;
font-family: sans-serif;
padding: 0.5em;
}
.ex2a .name {
color: black;
background: white;
font-family: "Marker Felt", cursive;
font-size: 45pt;
padding-top: 0.2em;
}
</style>
<div class="ex2a outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>
Aqui está a marcação. Isso é o que você escreveria hoje. Ela não use o Shadow DOM:
<style>
.outer {
border: 2px solid brown;
border-radius: 1em;
background: red;
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
}
.boilerplate {
color: white;
font-family: sans-serif;
padding: 0.5em;
}
.name {
color: black;
background: white;
font-family: "Marker Felt", cursive;
font-size: 45pt;
padding-top: 0.2em;
}
</style>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>
Como a árvore do DOM não possui encapsulamento, toda a estrutura do tag de nome é exposta ao documento. Se outros elementos da página acidentalmente os mesmos nomes de classe para estilo ou script, estamos não vamos nos passar.
Podemos evitar ter um momento ruim.
Etapa 1: ocultar detalhes da apresentação
Semanticamente, provavelmente só importa:
- É uma tag de nome.
- O nome é “Bob”.
Primeiro, escrevemos uma marcação mais próxima da verdadeira semântica que queremos:
<div id="nameTag">Bob</div>
Em seguida, colocamos todos os estilos e divs usados para apresentação em
um elemento <template>
:
<div id="nameTag">Bob</div>
<template id="nameTagTemplate">
<span class="unchanged"><style>
.outer {
border: 2px solid brown;
… same as above …
</style>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div></span>
</template>
Neste ponto, "Bob" é a única coisa renderizada. Como nós
moveu os elementos DOM de apresentação para dentro
um elemento <template>
, eles não serão renderizados,
elas podem ser acessadas em JavaScript. Fazemos isso agora para
preencha a raiz paralela:
<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
Agora que configuramos uma raiz paralela, a tag de nome é renderizada de novo. Se você clicasse com o botão direito na tag de nome e inspecionasse você vê que é uma marcação agradável e semântica:
<div id="nameTag">Bob</div>
Isso demonstra que, usando o Shadow DOM, ocultamos os de apresentação da tag de nome do documento. A os detalhes da apresentação são encapsulados no Shadow DOM.
Etapa 2: separar o conteúdo da apresentação
Nossa tag de nome agora oculta da página os detalhes da apresentação, não separa apresentação do conteúdo, porque, o conteúdo (o nome "Bob") está na página, o nome que é renderizado que copiamos para a raiz paralela. Se quisermos mudar na tag de nome, precisaríamos fazer isso em dois lugares, e eles podem saiam de sincronia.
Os elementos HTML são composicionais: você pode colocar um botão dentro de uma tabela, por exemplo. A composição é o que precisamos aqui: a tag de nome deve ser um composição do fundo vermelho, o “Oi!” o texto e o conteúdo que está na tag de nome.
Você, autor do componente, define como a composição funciona com seu
widget usando um novo elemento chamado <content>
. Isso
cria um ponto de inserção na apresentação do widget e o
o ponto de inserção seleciona conteúdo do host sombra para apresentar
naquele momento.
Se mudarmos a marcação no Shadow DOM para esta:
<span class="unchanged"><template id="nameTagTemplate">
<style>
…
</style></span>
<div class="outer">
<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
<content></content>
</div>
</div>
<span class="unchanged"></template></span>
Quando a tag de nome é renderizada, o conteúdo do host sombra é
projetada no local em que o elemento <content>
aparece.
Agora, a estrutura do documento está mais simples, porque o nome é apenas em um só lugar: o documento. Se sua página precisar atualizar do usuário, basta escrever:
document.querySelector('#nameTag').textContent = 'Shellie';
e pronto. A renderização da tag de nome é atualizada automaticamente
pelo navegador, porque estamos projetando o conteúdo do
no lugar com <content>
.
<div id="ex2b">
Agora fizemos a separação do conteúdo e da apresentação. O o conteúdo está no documento. a apresentação está no Shadow DOM. Eles são sincronizados automaticamente pelo navegador quando chega a hora para renderizar algo.
Etapa 3: lucro
Ao separar o conteúdo e a apresentação, podemos simplificar a
que manipula o conteúdo, no exemplo da tag de nome, que
código só precisa lidar com uma estrutura simples que contém
um <div>
em vez de vários.
Agora, se mudarmos nossa apresentação, não precisamos alterar nenhum dos o código-fonte é alterado.
Por exemplo, digamos que queremos localizar nossa tag de nome. Ainda é um nome tag para que o conteúdo semântico no documento não mude:
<div id="nameTag">Bob</div>
O código de configuração da raiz paralela permanece o mesmo. Apenas o que é colocado mudanças na raiz paralela:
<template id="nameTagTemplate">
<style>
.outer {
border: 2px solid pink;
border-radius: 1em;
background: url(sakura.jpg);
font-size: 20pt;
width: 12em;
height: 7em;
text-align: center;
font-family: sans-serif;
font-weight: bold;
}
.name {
font-size: 45pt;
font-weight: normal;
margin-top: 0.8em;
padding-top: 0.2em;
}
</style>
<div class="outer">
<div class="name">
<content></content>
</div>
と申します。
</div>
</template>
Essa é uma grande melhoria em relação à situação atual na Web, porque seu código de atualização de nome pode depender da estrutura component que é simples e consistente. Seu nome o código de atualização não precisa conhecer a estrutura usada para renderização. Se considerarmos o que é renderizado, o nome aparecerá segundo em inglês (depois de “Hi! Meu nome é"), mas primeiro em japonês (antes de "申ます"). Essa distinção é semanticamente sem sentido do ponto de vista da atualização do nome exibido, para que o código de atualização do nome não precise saber desses detalhes.
Crédito extra: projeção avançada
No exemplo acima, o elemento <content>
seleciona a dedo todo o conteúdo do host sombra. Ao usar o método
select
, é possível controlar quais
de um elemento de conteúdo. Você também pode usar vários conteúdos
os elementos.
Por exemplo, se você tiver um documento que contenha isto:
<div id="nameTag">
<div class="first">Bob</div>
<div>B. Love</div>
<div class="email">bob@</div>
</div>
e uma raiz paralela que usa seletores de CSS para selecionar conteúdo específico:
<div style="background: purple; padding: 1em;">
<div style="color: red;">
<content **select=".first"**></content>
</div>
<div style="color: yellow;">
<content **select="div"**></content>
</div>
<div style="color: blue;">
<content **select=".email">**</content>
</div>
</div>
O elemento <div class="email">
é correspondido por ambos
os elementos <content select="div">
e <content
select=".email">
. Quantas vezes o e-mail de Beto envia
e em quais cores?
A resposta é que o endereço de e-mail de Bob aparece uma vez e está amarelo.
O motivo é que, como as pessoas que invadem no Shadow DOM sabem,
e construir a árvore do que é realmente renderizado na tela é como uma
uma festa enorme. O elemento de conteúdo é o convite que permite
conteúdo do documento para a renderização dos bastidores do Shadow DOM
parte. Os convites são entregues em ordem: quem recebe
do convite depende de para quem ele é dirigido (ou seja,
o atributo select
. Conteúdo, uma vez
convidado, sempre aceita o convite (quem não aceitaria?!) e desativou-o.
vai Se um convite subsequente for enviado para esse endereço novamente,
Não há ninguém em casa nem ele vai na sua festa.
No exemplo acima, <div class="email">
corresponde a
o seletor div
e o .email
seletor, mas como o elemento de conteúdo com o elemento div
é exibido no início do documento,
<div class="email">
vai para a festa amarela e
ninguém está disponível para comparecer à festa azul. Isso pode
por que é tão azul, embora a tristeza ame companhia,
nunca se sabe.
Se um item for convidado para não partes, ele não será renderizados. Foi isso que aconteceu com o texto “Hello, world” na no primeiro exemplo. Isso é útil quando você deseja obter renderização radicalmente diferente: escreva o modelo semântico na que é acessível para os scripts na página, mas ocultam para fins de renderização e conectá-lo a uma interface no Shadow DOM usando JavaScript.
Por exemplo, o HTML tem um bom seletor de data. Se você escrever para <input
type="date">
, vai receber um ótimo calendário pop-up. Mas e se você
Permitir que o usuário escolha um período para a sobremesa
férias na ilha (você sabe... com redes feitas de Red Vines). Você
configure seu documento da seguinte forma:
<div class="dateRangePicker">
<label for="start">Start:</label>
<input type="date" name="startDate" id="start">
<br>
<label for="end">End:</label>
<input type="date" name="endDate" id="end">
</div>
mas criar um Shadow DOM que use uma tabela para criar um calendário elegante que destaca o intervalo de datas e assim por diante. Quando o usuário clica os dias no calendário, o componente atualiza o estado no entradas startDate e endDate; quando o usuário enviar o formulário, a valores desses elementos de entrada são enviados.
Por que incluí rótulos no documento se eles não seriam renderizado? Isso porque, se um usuário abrir o formulário em um navegador, que não seja compatível com o Shadow DOM, o formulário ainda poderá ser usado, mas não bonitos. O usuário vê algo como:
<div class="dateRangePicker">
<label for="start">Start:</label>
<input type="date" name="startDate" id="start">
<br>
<label for="end">End:</label>
<input type="date" name="endDate" id="end">
</div>
Introdução ao Shadow DOM
Esses são os fundamentos do Shadow DOM: você transmite o Shadow DOM 101! Você pode fazer mais com o Shadow DOM, por exemplo, podendo usar múltiplos elementos um host sombra ou sombras aninhadas para encapsulamento ou sua página usando visualizações orientadas a modelos (MDV, na sigla em inglês) e o Shadow DOM. E Web Os componentes são mais do que apenas o Shadow DOM.
Vamos explicar isso em outras postagens.