DOM: árvore viva da interface

O navegador parseia HTML e constrói o DOM (Document Object Model). JavaScript lê e altera essa árvore em resposta a eventos — cliques, submits, timers, respostas de API. Este é o mecanismo por trás de dashboards, formulários dinâmicos e SPAs.

Seleção e cache de elementos

const form = document.querySelector('#filtro-relatorio');
const tbody = document.querySelector('#tabela-corpo');
const feedback = document.querySelector('#feedback');

Query repetido em loop custa performance; selecione uma vez fora do loop ou use event delegation.

Eventos e preventDefault

form.addEventListener('submit', (event) => {
  event.preventDefault();
  const dados = new FormData(form);
  const termo = String(dados.get('busca') || '').trim().toLowerCase();
  filtrarLinhas(tbody, termo);
  feedback.textContent = termo ? `Filtrando: ${termo}` : 'Exibindo todos';
});
Diagrama PlantUML

Padrão state + render

const state = {
  contador: 0,
  limite: 10
};

function render() {
  document.querySelector('#valor').textContent = state.contador;
  document.querySelector('#btn-inc').disabled = state.contador >= state.limite;
}

document.querySelector('#btn-inc').addEventListener('click', () => {
  if (state.contador < state.limite) {
    state.contador += 1;
    render();
  }
});

render();

Estado canônico vive em objeto; DOM é projeção. Frameworks (React, Vue) industrializam esse padrão — entender manualmente primeiro evita “magia” opaca depois.

Event delegation

document.querySelector('#lista-tarefas').addEventListener('click', (e) => {
  const btn = e.target.closest('[data-action="concluir"]');
  if (!btn) return;
  btn.closest('li')?.classList.toggle('concluida');
});

Fetch e renderização assíncrona

async function carregarIndicadores() {
  const alvo = document.querySelector('#indicadores');
  alvo.textContent = 'Carregando...';

  try {
    const res = await fetch('/api/indicadores');
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const dados = await res.json();
    alvo.innerHTML = dados.map(d =>
      `<div class="card"><strong>${d.nome}</strong>: ${d.valor}</div>`
    ).join('');
  } catch (erro) {
    alvo.textContent = `Erro: ${erro.message}`;
  }
}

Debounce em buscas

function debounce(fn, ms = 300) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  };
}

const buscar = debounce((termo) => filtrarLinhas(tbody, termo), 300);
input.addEventListener('input', (e) => buscar(e.target.value));

Ciclo de vida da página e ordem de execução

HTML parseia de cima para baixo. Scripts sem defer ou async bloqueiam renderização. Com defer, o script executa após DOM pronto — padrão recomendado para app.js no final do body ou no head com defer.

Diagrama PlantUML

Atualizações dinâmicas e acessibilidade

Ao injetar conteúdo com innerHTML, leitores de tela podem não anunciar mudança. Para feedback importante (erro de formulário, resultado de busca), use aria-live="polite" em região dedicada:

<div id="feedback" role="status" aria-live="polite"></div>

Atualize textContent dessa região em vez de só mudar cor de borda — usuários de tecnologia assistiva dependem disso.

Segurança: innerHTML e dados externos

Nunca concatene HTML com strings vindas de usuário ou API sem sanitizar — risco de XSS. Para texto simples, use textContent. Para HTML confiável, considere bibliotecas como DOMPurify (como esta plataforma faz na importação Markdown).

Exercício: lista de tarefas completa

Implemente CRUD mínimo: adicionar tarefa, marcar concluída, remover, filtrar (todas/ativas/concluídas). Estado em objeto único; função render() sincroniza DOM. Persista em localStorage. Critério de pronto: recarregar página mantém lista; teclado consegue submeter formulário com Enter.

Ponte para frameworks

React, Vue e Svelte automatizam “state + render” e diff eficiente da árvore. Entender o padrão manual evita tratar framework como caixa preta: quando algo quebra, você ainda sabe que o problema é estado desincronizado ou listener duplicado.

Para aprofundar na web

Para entender melhor este tema, pesquise por:

  • "DOM JavaScript MDN introdução" — árvore de nós e APIs principais
  • "event delegation JavaScript explicado" — listeners em listas dinâmicas
  • "preventDefault submit formulário" — controle de envio sem reload
  • "fetch API MDN tutorial" — requisições HTTP no navegador
  • "debounce input search JavaScript" — evitar chamadas excessivas ao digitar

Implemente filtro de lista sem framework — só DOM API — antes de aprender React.

Atividades

  1. preventDefault no submit impede:

    • A) Propagação
    • B) Reload da página
    • C) Execução do handler
    • D) Validação HTML5
    Ver resposta

    Resposta correta: B) Reload da página

    Cancela comportamento nativo do formulário.

  2. Event delegation é útil quando:

    • A) Lista estática pequena
    • B) Itens são adicionados dinamicamente
    • C) Não há cliques
    • D) CSS Grid off
    Ver resposta

    Resposta correta: B) Itens são adicionados dinamicamente

    Listener no ancestral captura eventos de filhos presentes ou futuros.

  3. Explique state vs DOM no padrão render().

    Ver resposta

    state guarda dados canônicos; render sincroniza UI. Mudanças passam por atualizar state e chamar render, evitando divergência entre lógica e tela.