Manipulação do DOM com JavaScript
Aprenda a manipular o DOM com JavaScript: altere conteúdo com textContent e innerHTML, edite atributos e classes, crie, insira, clone e remova elementos, com exemplos práticos.
A manipulação do DOM (Document Object Model) com JavaScript é uma habilidade fundamental para desenvolvedores web. O DOM é uma representação em árvore, dinâmica e atualizada em tempo real, da página que o navegador constrói a partir do seu HTML; manipulá-lo significa usar JavaScript para ler e modificar essa árvore em tempo de execução, fazendo a página ser atualizada sem recarregamento.
Este guia aborda as quatro operações mais comuns:
- Leitura e alteração de conteúdo com
textContenteinnerHTML. - Leitura e alteração de atributos com
getAttribute,setAttributeeclassList. - Criação e inserção de elementos com
createElement,append,insertBeforeeinsertAdjacentHTML. - Remoção e substituição de elementos com
remove()ereplaceWith().
Antes de manipular um elemento, você precisa primeiro encontrá-lo. Se você ainda não domina getElementById, querySelector e métodos similares, leia Selecionando Elementos do DOM e Buscando: getElement, querySelector primeiro — todos os exemplos abaixo pressupõem que você já tem uma referência ao nó.
Alterando Conteúdo e Atributos de Elementos
Manipular o conteúdo e os atributos de elementos do DOM é um aspecto fundamental do desenvolvimento web dinâmico. Ao alterar o conteúdo, podemos atualizar o texto ou HTML dentro de um elemento. Ao modificar atributos, podemos alterar propriedades como class, id ou src. O JavaScript oferece métodos poderosos para realizar essas tarefas, permitindo criar aplicações web responsivas e interativas. Vamos explorar como usar innerHTML, textContent, setAttribute e getAttribute de forma eficaz.
Alterando o Conteúdo de Elementos
Podemos alterar o conteúdo de um elemento usando as propriedades innerHTML ou textContent.
innerHTML vs textContent
innerHTML nos permite definir ou obter o conteúdo HTML de um elemento, incluindo quaisquer tags HTML.
<!DOCTYPE html>
<html>
<head>
<title>innerHTML vs textContent</title>
<style>
.content-container {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.html-content {
color: red;
}
</style>
</head>
<body>
<div class="content-container">
<p id="content">Original paragraph content.</p>
<button id="change-innerHTML">Change using innerHTML</button>
<button id="change-textContent">Change using textContent</button>
</div>
<script>
const content = document.getElementById('content');
const innerHTMLButton = document.getElementById('change-innerHTML');
const textContentButton = document.getElementById('change-textContent');
innerHTMLButton.addEventListener('click', () => {
content.innerHTML = 'New content with <strong class="html-content">HTML</strong> tags.';
});
textContentButton.addEventListener('click', () => {
content.textContent = 'Updated paragraph content without HTML tags.';
});
</script>
</body>
</html>Explicação:
innerHTMLnos permite definir ou obter o conteúdo HTML de um elemento, incluindo quaisquer tags HTML. No exemplo, clicar no botão "Change using innerHTML" substituirá o conteúdo do parágrafo por um novo conteúdo que inclui tags HTML, alterando a aparência do texto.textContentdefine ou obtém o conteúdo de texto de um elemento sem processar tags HTML. Clicar no botão "Change using textContent" substituirá o conteúdo do parágrafo por texto simples, ignorando quaisquer tags HTML.
Use textContent ao inserir conteúdo gerado pelo usuário para evitar riscos de segurança como XSS (Cross-Site Scripting). Atribuir uma string bruta de um usuário ao innerHTML permite que um atacante injete <script> ou atributos de manipuladores de eventos que são executados na sua página. textContent nunca interpreta HTML, portanto a string é exibida literalmente e é sempre segura.
Existe também uma terceira propriedade, outerHTML, que representa o elemento e sua própria tag. Atribuir a ela substitui o elemento completamente:
// Replace the whole <p> with an <h2>, keeping it in the same position
const p = document.querySelector('p');
p.outerHTML = '<h2>A heading instead</h2>';
// After this, `p` still points at the old, detached node — re-query if you need the new one.Alterando Atributos de Elementos
Podemos alterar os atributos de um elemento usando o método setAttribute e recuperá-los usando o método getAttribute. Esses métodos são úteis para modificar elementos dinamicamente com base em interações do usuário ou outros eventos.
<!DOCTYPE html>
<html>
<head>
<title>setAttribute and getAttribute</title>
</head>
<body>
<div id="container" class="initial-class">Container content</div>
<button id="change-attribute">Change Attribute</button>
<button id="get-attribute">Get Attribute</button>
<script>
const container = document.getElementById('container');
const changeAttributeButton = document.getElementById('change-attribute');
const getAttributeButton = document.getElementById('get-attribute');
changeAttributeButton.addEventListener('click', () => {
container.setAttribute('class', 'new-class');
alert('Class attribute changed to "new-class"');
});
getAttributeButton.addEventListener('click', () => {
const className = container.getAttribute('class');
alert(`Current class attribute: ${className}`);
});
</script>
</body>
</html>Explicação:
setAttribute('attributeName', 'value'): Este método nos permite definir um novo valor para um atributo especificado de um elemento. No exemplo, clicar no botão "Change Attribute" altera o atributo class do<div>de "initial-class" para "new-class".getAttribute('attributeName'): Este método recupera o valor atual do atributo especificado. Clicar no botão "Get Attribute" exibe um alerta com o valor atual do atributo class do<div>.
Sempre use setAttribute e getAttribute para atributos personalizados e atributos gerados dinamicamente. Para manipulação de classes, prefira a API classList abaixo — ela edita classes individualmente em vez de sobrescrever toda a string class.
Atributos vs. propriedades
Uma fonte comum de confusão: um atributo HTML (o que está escrito na marcação) nem sempre é o mesmo que a propriedade do DOM (o valor dinâmico no objeto JavaScript). getAttribute('value') lê o valor original da marcação, enquanto input.value lê o que o usuário digitou atualmente. Para atributos boolean a diferença é mais marcante — checkbox.getAttribute('checked') reflete a marcação, enquanto checkbox.checked reflete o estado dinâmico. Como regra: use propriedades (el.id, el.value, el.checked) para valores padrão que mudam frequentemente, e setAttribute/getAttribute para atributos data- personalizados ou atributos de marcação pontuais.
Para ler e escrever atributos data-* existe uma API dedicada e ergonômica — a propriedade dataset:
// <div id="card" data-user-id="42" data-role="admin"></div>
const card = document.getElementById('card');
console.log(card.dataset.userId); // "42" (note: data-user-id → userId)
card.dataset.role = 'editor'; // writes data-role="editor"Gerenciando classes com classList
classList alterna classes individuais sem perturbar as outras já presentes:
element.classList.add('new-class'); // add one (or several) classes
element.classList.remove('old-class'); // remove a class
element.classList.toggle('active'); // add if absent, remove if present
element.classList.replace('open', 'shut'); // swap one class for another
console.log(element.classList.contains('active')); // true / falsetoggle aceita um segundo argumento opcional para forçar um estado, o que é útil para sincronizar uma classe com uma condição: el.classList.toggle('valid', isValid). Para trabalhos de estilização mais avançados — incluindo leitura e escrita da propriedade style — veja Trabalhando com Estilos no DOM.
Adicionando e Removendo Elementos
Podemos adicionar novos elementos ao DOM ou remover elementos existentes.
Adicionando Elementos
Para adicionar novos elementos ao DOM, primeiro os criamos usando o método createElement, depois os anexamos a um elemento existente usando appendChild ou os inserimos em uma posição específica usando insertBefore.
createElement(), appendChild(), insertBefore()
<!DOCTYPE html>
<html>
<head>
<title>Adding Elements</title>
</head>
<body>
<div id="task-list">
<h2>Task List</h2>
<ul id="tasks">
<li>Initial task</li>
</ul>
<input type="text" id="new-task" placeholder="New task">
<button id="add-task">Add Task</button>
<button id="insert-before">Insert Before First Task</button>
</div>
<script>
const taskList = document.getElementById('tasks');
const newTaskInput = document.getElementById('new-task');
const addTaskButton = document.getElementById('add-task');
const insertBeforeButton = document.getElementById('insert-before');
addTaskButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
taskList.appendChild(newTask);
newTaskInput.value = '';
}
});
insertBeforeButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
const firstTask = taskList.firstElementChild;
taskList.insertBefore(newTask, firstTask);
}
});
</script>
</body>
</html>Explicação:
createElement('tagName'): Este método cria um novo elemento especificado portagName. Por exemplo,document.createElement('li')cria um novo elemento<li>.appendChild(newElement): Este método anexa um novo elemento filho a um elemento pai especificado. No exemplo, clicar no botão "Add Task" cria um novo item de lista (<li>) e o adiciona à lista de tarefas (<ul>).insertBefore(newElement, referenceElement): Este método insere um novo elemento antes de um elemento de referência especificado dentro do mesmo pai. Clicar no botão "Insert Before First Task" cria um novo item de lista (<li>) e o insere antes da primeira tarefa na lista.
Para inserir strings HTML diretamente, considere insertAdjacentHTML(). Para substituir um elemento existente, element.replaceWith(newElement) é uma alternativa moderna a combinar remove() e appendChild().
insertAdjacentHTML e os métodos de inserção modernos
Quando você já tem uma string HTML (em vez de um nó construído com createElement), insertAdjacentHTML(position, html) a analisa e insere em uma única chamada. O argumento position é uma de quatro palavras-chave relativas ao elemento:
// Given: <div id="box">content</div>
const box = document.getElementById('box');
box.insertAdjacentHTML('beforebegin', '<p>before the box</p>'); // before <div>
box.insertAdjacentHTML('afterbegin', '<p>first child</p>'); // inside, at the start
box.insertAdjacentHTML('beforeend', '<p>last child</p>'); // inside, at the end
box.insertAdjacentHTML('afterend', '<p>after the box</p>'); // after </div>Os navegadores modernos também fornecem append(), prepend(), before() e after(), que aceitam nós ou strings simples e podem receber vários argumentos de uma vez — geralmente são mais claros do que appendChild/insertBefore:
const list = document.getElementById('tasks');
const item = document.createElement('li');
item.textContent = 'New task';
list.append(item, 'some trailing text'); // append node + string together
list.prepend('Top of the list'); // insert at the startClonando elementos
Para duplicar um nó existente em vez de criá-lo do zero, use cloneNode(deep). Passe true para copiar o elemento junto com todos os seus descendentes; passe false (ou nada) para copiar apenas o elemento em si:
const template = document.getElementById('card');
const copy = template.cloneNode(true); // deep clone, including children
copy.id = 'card-2'; // ids must stay unique
document.body.append(copy);Para marcação repetida e complexa, o elemento <template> é a ferramenta criada especificamente para isso — seu conteúdo permanece inativo até você cloná-lo.
Removendo Elementos
Para remover um elemento, podemos usar o método moderno element.remove().
<!DOCTYPE html>
<html>
<head>
<title>Removing Elements</title>
</head>
<body>
<div id="container">
<p id="paragraph">This is a paragraph.</p>
<button id="remove-paragraph">Remove Paragraph</button>
</div>
<script>
const container = document.getElementById('container');
const paragraph = document.getElementById('paragraph');
const removeParagraphButton = document.getElementById('remove-paragraph');
removeParagraphButton.addEventListener('click', () => {
paragraph.remove();
});
</script>
</body>
</html>Explicação:
element.remove(): Este método moderno remove um elemento especificado do DOM diretamente. No exemplo, clicar no botão "Remove Paragraph" remove o elemento<p>da página.
Usar element.remove() é a abordagem recomendada para alterações dinâmicas de conteúdo, como remover itens de um carrinho de compras, pois oferece uma sintaxe mais limpa do que o método legado parent.removeChild(child) (que ainda é útil quando você precisa de uma referência ao nó removido).
Inserindo Muitos Elementos de Forma Eficiente
Cada vez que você insere um nó no documento ativo, o navegador pode precisar recalcular o layout e repintar a tela. Fazer isso dentro de um loop — uma vez por item — é ineficiente. Um DocumentFragment permite que você monte nós fora da tela e os insira todos em uma única operação:
const list = document.getElementById('tasks');
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 1000; i++) {
const li = document.createElement('li');
li.textContent = `Task ${i}`;
fragment.appendChild(li); // no layout work — fragment is not in the document
}
list.appendChild(fragment); // one insertion, one reflowDuas regras práticas relacionadas mantêm o código com muito DOM eficiente: agrupe suas leituras e escritas (não intercale a leitura de offsetHeight com a definição de estilos, ou você força reflows repetidos), e prefira construir uma string e atribuí-la a innerHTML uma vez a muitas chamadas pequenas de appendChild quando você não está anexando listeners de eventos a cada nó. Para um tratamento aprofundado, veja Otimização de Desempenho do DOM.
Exemplo: Lista de Tarefas Dinâmica
Vamos criar uma aplicação simples de lista de tarefas para demonstrar os conceitos acima.
<!DOCTYPE html>
<html>
<head>
<title>To-Do List</title>
</head>
<body>
<div id="todo-list">
<h2>My To-Do List</h2>
<ul id="tasks">
<li>Learn JavaScript</li>
</ul>
<input type="text" id="new-task" placeholder="New task">
<button id="add-task">Add Task</button>
</div>
<script>
const taskList = document.getElementById('tasks');
const newTaskInput = document.getElementById('new-task');
const addTaskButton = document.getElementById('add-task');
addTaskButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
taskList.appendChild(newTask);
newTaskInput.value = '';
}
});
taskList.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
event.target.remove();
}
});
</script>
</body>
</html>Explicação:
createElementcria um novo elemento de item de lista para a tarefa, eappendChildo adiciona à lista.- Um único listener de clique é anexado ao
<ul>, não a cada<li>. Como os cliques se propagam do item clicado para seus ancestrais, o listener inspecionaevent.targetpara decidir qual tarefa foi clicada e a remove. Esse padrão é chamado de delegação de eventos, e é a razão pela qual o handler continua funcionando para tarefas que não existiam quando a página foi carregada.
A delegação de eventos é o motivo pelo qual não adicionamos um listener separado cada vez que uma tarefa é criada — um único listener no pai lida com filhos atuais e futuros. Para se aprofundar em propagação, event.target vs event.currentTarget e delegação, leia Manipulação de Eventos no DOM.
Quando uma interface cresce para muitas peças de estado independentes e atualizadas frequentemente, manter o DOM em sincronia manualmente torna-se propenso a erros. Frameworks como React, Vue ou Svelte permitem que você descreva como a interface deve parecer para um determinado estado e cuidam das atualizações do DOM para você. As técnicas escritas à mão neste capítulo permanecem a base sobre a qual esses frameworks são construídos.
Conclusão
Dominar a manipulação do DOM é essencial para criar aplicações web dinâmicas e interativas. Agora você sabe como alterar conteúdo (textContent, innerHTML, outerHTML), trabalhar com atributos e classes (setAttribute, dataset, classList), criar e inserir nós (createElement, append, insertAdjacentHTML, cloneNode), removê-los e substituí-los (remove(), replaceWith()), e agrupar inserções de forma eficiente com um DocumentFragment.
Para continuar construindo sobre essas bases:
- Selecionando Elementos do DOM — encontre os nós que deseja alterar.
- Atravessando o DOM — mova-se entre pais, filhos e irmãos.
- Trabalhando com Estilos no DOM — leia e escreva CSS a partir do JavaScript.
- Manipulação de Eventos no DOM — responda à interação do usuário.