O Document Object Model (DOM) é uma interface de programação para documentos HTML e XML. Ele representa a página de forma que os programas possam manipular sua estrutura, estilo e conteúdo. O DOM é uma das ferramentas mais importantes para o desenvolvimento web com JavaScript, pois permite que você interaja dinamicamente com a página, alterando elementos, respondendo a eventos e muito mais.

Nesta aula, vamos explorar os conceitos fundamentais do DOM: o que é, como ele organiza os elementos em uma árvore de nós e o objeto document, que é a porta de entrada para toda a manipulação do DOM.

O que é o DOM

O DOM (Document Object Model) é uma representação em árvore de um documento HTML. Cada parte do documento (como tags, atributos, textos, comentários) é representada como um nó (node) nessa árvore. O DOM define uma série de interfaces e métodos para que possamos acessar e modificar esses nós programaticamente.

Quando o navegador carrega uma página HTML, ele analisa o código e constrói o DOM. Esse processo é chamado de parsing. O DOM resultante é um modelo de objetos que reflete exatamente a estrutura do documento. Por exemplo, considere o seguinte HTML:

<!DOCTYPE html>
<html>
  <head>
    <title>Minha Página</title>
  </head>
  <body>
    <h1>Olá, Mundo!</h1>
    <p>Este é um parágrafo.</p>
  </body>
</html>

O DOM correspondente a esse HTML teria a seguinte estrutura (simplificada):

Document
  └─ html
       ├─ head
       │    └─ title
       │         └─ "Minha Página"
       └─ body
            ├─ h1
            │    └─ "Olá, Mundo!"
            └─ p
                 └─ "Este é um parágrafo."

Cada elemento HTML se torna um nó do tipo Element, os textos dentro deles se tornam nós do tipo Text, e o documento todo é um nó do tipo Document. O DOM também inclui nós para atributos (Attr), comentários (Comment), entre outros.

Uma das grandes vantagens do DOM é que ele é independente de linguagem de programação. Embora seja mais comumente usado com JavaScript, existem implementações para Python, Java, C#, etc. No entanto, no contexto do navegador, o JavaScript é a linguagem padrão para manipular o DOM.

A árvore de nós

A árvore de nós (node tree) é a estrutura hierárquica que representa o documento. Cada nó tem um pai (parent), exceto o nó raiz (root), que é o próprio document. Os nós podem ter filhos (children), que são outros nós diretamente aninhados. Nós que estão no mesmo nível são chamados de irmãos (siblings).

Existem diferentes tipos de nós, mas os mais comuns são:

  • Document Node (Node.DOCUMENT_NODE): representa o documento inteiro. É a raiz da árvore.
  • Element Node (Node.ELEMENT_NODE): representa um elemento HTML, como <div>, <p>, etc.
  • Text Node (Node.TEXT_NODE): representa o texto dentro de um elemento.
  • Attribute Node (Node.ATTRIBUTE_NODE): representa um atributo de um elemento (embora na prática, atributos sejam acessados como propriedades do elemento).
  • Comment Node (Node.COMMENT_NODE): representa um comentário HTML (<!-- ... -->).

Cada nó possui propriedades que permitem navegar pela árvore. As principais são:

  • parentNode: retorna o nó pai.
  • childNodes: retorna uma lista (NodeList) de todos os nós filhos, incluindo text nodes, comment nodes, etc.
  • firstChild: retorna o primeiro nó filho.
  • lastChild: retorna o último nó filho.
  • nextSibling: retorna o próximo nó irmão.
  • previousSibling: retorna o nó irmão anterior.

Além disso, para elementos, existem propriedades mais específicas como children (apenas elementos filhos), firstElementChild, lastElementChild, etc.

Exemplo prático: suponha o seguinte HTML:

<div id="container">
  <p>Primeiro parágrafo</p>
  <p>Segundo parágrafo</p>
</div>

Podemos acessar os nós assim:

const container = document.getElementById('container');
console.log(container.childNodes); // NodeList contendo text nodes (espaços) e os elementos p
console.log(container.children);    // HTMLCollection contendo apenas os dois elementos p
console.log(container.firstChild);  // provavelmente um text node (espaço em branco)
console.log(container.firstElementChild); // o primeiro elemento p

É importante notar que os navegadores costumam inserir nós de texto para espaços em branco entre tags. Por isso, childNodes pode incluir esses text nodes, enquanto children ignora.

document

O objeto document é a interface principal para acessar e manipular o DOM. Ele representa a página carregada no navegador e fornece métodos para selecionar elementos, criar novos nós, modificar conteúdo, etc. document é uma propriedade global do objeto window, mas geralmente acessamos diretamente como document.

Algumas propriedades e métodos importantes do document incluem:

  • document.documentElement: retorna o elemento <html>.
  • document.head: retorna o elemento <head>.
  • document.body: retorna o elemento <body>.
  • document.title: obtém ou define o título da página.
  • document.URL: retorna a URL completa da página.
  • document.getElementById(id): seleciona um elemento pelo seu ID.
  • document.getElementsByClassName(className): seleciona elementos por classe (retorna HTMLCollection).
  • document.getElementsByTagName(tagName): seleciona elementos por nome da tag.
  • document.querySelector(selector): seleciona o primeiro elemento que corresponde a um seletor CSS.
  • document.querySelectorAll(selector): seleciona todos os elementos que correspondem a um seletor CSS (retorna NodeList).
  • document.createElement(tagName): cria um novo elemento.
  • document.createTextNode(text): cria um nó de texto.

Exemplo de uso básico:

// Acessando o título da página
console.log(document.title);

// Selecionando um elemento pelo ID
const header = document.getElementById('header');
header.style.color = 'red';

// Selecionando todos os parágrafos
const paragraphs = document.querySelectorAll('p');
paragraphs.forEach(p => p.style.fontSize = '16px');

// Criando um novo elemento e adicionando ao body
const newDiv = document.createElement('div');
newDiv.textContent = 'Olá, DOM!';
document.body.appendChild(newDiv);

O objeto document também possui eventos, como DOMContentLoaded, que é disparado quando o DOM está completamente carregado (antes de imagens e estilos). É comum usar esse evento para garantir que o DOM esteja pronto antes de manipulá-lo:

document.addEventListener('DOMContentLoaded', function() {
  // Código que manipula o DOM
  console.log('DOM carregado!');
});

Em resumo, document é a porta de entrada para toda a interação com a página. Dominar seus métodos e propriedades é essencial para qualquer desenvolvedor front-end.

Boas Práticas

Ao trabalhar com o DOM, algumas boas práticas ajudam a manter o código eficiente e legível:

  • Cache de seleções: se você for usar um elemento várias vezes, armazene-o em uma variável para evitar consultas repetidas ao DOM.
  • Evite manipulações frequentes: modificar o DOM muitas vezes seguidas pode causar reflows e repaints, prejudicando a performance. Prefira construir um fragmento (DocumentFragment) e inseri-lo de uma vez.
  • Use métodos modernos: prefira querySelector e querySelectorAll em vez de métodos mais antigos como getElementsByClassName quando precisar de flexibilidade com seletores CSS.
  • Event delegation: ao adicionar eventos a muitos elementos, considere usar a delegação de eventos no pai para melhor performance.

Referências

Exercícios

  1. Explique com suas palavras o que é o DOM e qual a sua importância para o desenvolvimento web com JavaScript.

    ✓ Resposta: O DOM (Document Object Model) é uma representação em árvore do documento HTML que o navegador cria. Ele permite que programas (como JavaScript) acessem e manipulem a estrutura, o estilo e o conteúdo da página de forma dinâmica. É importante porque possibilita criar páginas interativas, responder a eventos do usuário e atualizar o conteúdo sem recarregar a página.
  2. Dado o seguinte HTML, desenhe a árvore de nós simplificada (apenas elementos e text nodes relevantes):

    <div class="container">
      <h1>Título</h1>
      <p>Texto</p>
    </div>

    ✓ Resposta: Árvore de nós:
    Document
      └─ html
           └─ body
                └─ div.container
                     ├─ h1
                     │    └─ "Título"
                     └─ p
                          └─ "Texto"
  3. Escreva um código JavaScript que selecione o elemento com id "meu-id" e altere seu texto para "Novo texto".

    ✓ Resposta:
    const elemento = document.getElementById('meu-id');
    if (elemento) {
      elemento.textContent = 'Novo texto';
    }
  4. Qual a diferença entre childNodes e children? Dê um exemplo de quando isso pode ser relevante.

    ✓ Resposta: childNodes retorna uma NodeList com todos os nós filhos, incluindo text nodes (espaços, quebras de linha) e comment nodes. Já children retorna uma HTMLCollection apenas com os elementos filhos (nós do tipo Element). A diferença é relevante quando se deseja iterar apenas sobre elementos, ignorando espaços em branco, ou quando se precisa acessar text nodes explicitamente.
  5. Utilizando o objeto document, crie um novo elemento <li> com o texto "Item 3" e adicione-o ao final de uma lista <ul id="lista"> que já contém dois itens.

    ✓ Resposta:
    const lista = document.getElementById('lista');
    const novoItem = document.createElement('li');
    novoItem.textContent = 'Item 3';
    lista.appendChild(novoItem);