W3docs

Técnicas Avançadas de DOM

Dominar técnicas avançadas de DOM pode aprimorar significativamente suas habilidades de desenvolvimento web, permitindo criar código mais dinâmico, modular e fácil de manter.

Dominar técnicas avançadas de DOM ajuda a construir interfaces dinâmicas, modulares e de fácil manutenção com JavaScript puro — sem necessidade de framework. Este guia aborda dois pilares do trabalho moderno com componentes: o elemento <template> para definir marcação reutilizável e inerte, e o Shadow DOM para encapsular a estrutura, os estilos e o comportamento de um componente. Juntos, eles formam a base que alimenta os web components e os custom elements.

Criando e Usando Templates

Por que os Templates Existem

Antes do <template>, os desenvolvedores construíam marcação reutilizável colocando HTML em elementos <div> ocultos, literais de string JavaScript ou blocos <script type="text/template">. Cada abordagem tem uma desvantagem: <div>s ocultos ainda custam ao navegador análise e carregamento de recursos (imagens carregam, scripts executam), e templates em string perdem o destaque de sintaxe e são fáceis de quebrar.

O elemento <template> resolve isso. Seu conteúdo é analisado, mas inerte: o navegador constrói os nós do DOM, mas não os renderiza, não executa seus scripts e não carrega suas imagens ou mídia até que você clone explicitamente o conteúdo no documento ativo. Isso faz do <template> a ferramenta correta para declarar marcação que você pretende instanciar muitas vezes.

Usando o Elemento <template>

O elemento <template> permite definir HTML que não é renderizado quando a página carrega. Você acessa seu conteúdo por meio da propriedade somente leitura content, que retorna um DocumentFragment.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Using the <template> Element</title>
</head>
<body>
    <template id="my-template">
        <div class="card">
            <h2>Title</h2>
            <p>Content goes here...</p>
        </div>
    </template>
    <button id="show-template">Show Template</button>
    <div id="content"></div>

    <script>
        document.getElementById('show-template').addEventListener('click', () => {
            const template = document.getElementById('my-template');
            const content = document.getElementById('content');
            const clone = template.content.cloneNode(true);
            content.appendChild(clone);
        });
    </script>
</body>
</html>

Este exemplo demonstra a estrutura básica de um elemento <template> contendo um cartão com título e conteúdo. O conteúdo do template é clonado e inserido no DOM quando o botão é clicado. Para um mergulho mais profundo no elemento por conta própria, consulte o elemento <template>.

Clonando e Inserindo Conteúdo de Template

Para reutilizar um <template>, clone seu content e insira o clone no DOM. Sempre passe true para cloneNode para que toda a subárvore (o elemento e todos os seus descendentes) seja copiada — cloneNode(false) copia apenas o nó do topo e resultaria em um fragmento vazio.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Cloning and Inserting Template Content</title>
</head>
<body>
    <template id="card-template">
        <div class="card">
            <h2 class="title"></h2>
            <p class="body"></p>
        </div>
    </template>

    <button id="add-card">Add Card</button>
    <div id="container"></div>

    <script>
        let count = 0;
        document.getElementById('add-card').addEventListener('click', () => {
            const template = document.getElementById('card-template');
            const clone = template.content.cloneNode(true);

            // Fill the clone with dynamic data before inserting it.
            count++;
            clone.querySelector('.title').textContent = 'Card ' + count;
            clone.querySelector('.body').textContent = 'Created at ' + new Date().toLocaleTimeString();

            document.getElementById('container').appendChild(clone);
        });
    </script>
</body>
</html>

O valor real dos templates está neste padrão: clonar e depois popular o clone com dados antes de inseri-lo. Alguns detalhes que vale lembrar:

  • Um DocumentFragment se esvazia quando é adicionado. Após appendChild(clone), os filhos do fragmento se movem para o contêiner e o fragmento fica vazio — portanto, chame cloneNode uma vez por item que você deseja adicionar.
  • Consulte o clone, não o documento. Seletores como clone.querySelector('.title') operam no fragmento ainda não inserido, então você o preenche antes de chegar ao DOM ativo (evitando reflows extras). Veja buscando com querySelector.
  • document.importNode(template.content, true) é o equivalente entre documentos — use-o quando o template estiver em outro documento ou iframe para que os nós importados pertençam ao documento atual.

Shadow DOM

Introdução ao Shadow DOM

O Shadow DOM é um padrão web que permite o encapsulamento em web components. Ele anexa uma árvore DOM separada e oculta — a shadow tree — a um elemento (o shadow host). Os nós dentro dessa árvore não são acessíveis pelo document.querySelector normal da página, e os estilos definidos dentro dela não vazam para o restante da página. Isso mantém a estrutura interna, os estilos e o comportamento de um componente isolados do documento global.

Alguns termos que você encontrará ao longo do texto:

  • Shadow host — o elemento regular ao qual a shadow tree está anexada.
  • Shadow root — o nó raiz da shadow tree, retornado por attachShadow().
  • Shadow boundary — a fronteira entre a shadow tree e o restante do documento que o escopo não atravessa.

Modo Aberto vs. Fechado

attachShadow() exige uma opção mode:

const open = host.attachShadow({ mode: 'open' });
// host.shadowRoot  →  the shadow root (accessible from outside)

const closed = host2.attachShadow({ mode: 'closed' });
// host2.shadowRoot →  null (the root is hidden from outside scripts)

Na prática, prefira open. O modo closed não oferece segurança real — qualquer pessoa pode sobrescrever attachShadow antes do seu código ser executado — e só torna seu componente mais difícil de testar e depurar.

Encapsulamento e Desenvolvimento Baseado em Componentes

O encapsulamento garante que os estilos e scripts definidos dentro de um componente não vazem e afetem o restante do documento — e que estilos externos não penetrem nele. O exemplo abaixo anexa um shadow root e constrói seus conteúdos em um DocumentFragment para que toda a subárvore seja inserida em uma única operação. Um elemento <slot> projeta o conteúdo existente do host ("light DOM") na shadow tree junto com a própria marcação do componente.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Shadow DOM Example</title>
    <style>
        .card {
            padding: 20px;
            margin: 10px;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <div id="shadow-host" class="card">
        <span>This is the light DOM content</span>
    </div>

    <script>
        const host = document.getElementById('shadow-host');
        const shadowRoot = host.attachShadow({ mode: 'open' });

        const fragment = document.createDocumentFragment();
        const style = document.createElement('style');
        style.textContent = `.shadow-card { padding: 20px; margin: 10px; border: 1px solid blue; color: blue; }`;
        const slot = document.createElement('slot');
        const card = document.createElement('div');
        card.className = 'shadow-card';
        card.textContent = 'This is inside the Shadow DOM';

        fragment.appendChild(style);
        fragment.appendChild(slot);
        fragment.appendChild(card);
        shadowRoot.appendChild(fragment);
    </script>
</body>
</html>

Este exemplo cria uma shadow tree no #shadow-host e injeta estilos e conteúdo nela. O conteúdo do light DOM (This is the light DOM content) permanece no host e é exposto dentro da shadow tree por meio do elemento <slot>, aparecendo junto ao conteúdo shadow em vez de ser substituído por ele.

Observe o que o encapsulamento faz e não faz com os estilos. A regra .shadow-card vive dentro da shadow tree e estiliza apenas os nós dessa árvore; ela não pode corresponder a .card em outros lugares da página, e uma regra .card na página não consegue acessar a shadow tree. A única exceção são as propriedades herdáveiscolor, font-family, line-height e similares — que ainda fluem pela fronteira para o conteúdo do slot. O encapsulamento bloqueia a correspondência de seletores, não a herança. Para se aprofundar, veja estilizando o Shadow DOM e slots e composição.

Quando Usar Cada Técnica

  • Use <template> sempre que instanciar a mesma marcação repetidamente (linhas de lista, cartões, modais) e quiser defini-la declarativamente em HTML.
  • Use Shadow DOM quando um widget precisar de estilos que não devem colidir com a página host — um botão de sistema de design, um seletor de datas, um widget incorporável.
  • Combine ambos — defina a marcação em um <template> e clone-a em um shadow root — para construir custom elements totalmente reutilizáveis.

Boas Práticas

  • Prefira DocumentFragment para inserções em lote: Adicionar um fragmento a um shadow root (ou qualquer contêiner) em uma única operação minimiza recálculos de layout e melhora o desempenho de renderização.
  • Popule clones antes de inseri-los: Consulte e preencha o fragmento clonado enquanto ele ainda está desanexado, para que o navegador execute apenas um reflow ao ser adicionado.
  • Escolha o modo open para shadows: Mantém os componentes depuráveis e testáveis; closed não oferece segurança real.
  • Use document.importNode() entre documentos: Ao clonar conteúdo de outro documento ou iframe, importNode garante a propriedade correta do nó e evita erros entre documentos.
  • Mantenha o light DOM mínimo: Use elementos <slot> para projetar apenas o conteúdo que genuinamente pertence à página, mantendo o host previsível.
Informação

Aproveite o Shadow DOM para encapsular estilos e funcionalidades dentro dos componentes, evitando conflitos de estilos e garantindo código modular e de fácil manutenção.

Conclusão

Técnicas avançadas de DOM, como o uso de templates e Shadow DOM, são ferramentas poderosas para criar aplicações web modulares, de fácil manutenção e eficientes. Ao encapsular estilos e comportamentos de componentes e utilizar templates reutilizáveis, você pode aprimorar seu fluxo de desenvolvimento e construir aplicações web robustas.

Prática

Prática
Quais das seguintes afirmações sobre técnicas avançadas de DOM são verdadeiras?
Quais das seguintes afirmações sobre técnicas avançadas de DOM são verdadeiras?
Was this page helpful?