W3docs

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 textContent e innerHTML.
  • Leitura e alteração de atributos com getAttribute, setAttribute e classList.
  • Criação e inserção de elementos com createElement, append, insertBefore e insertAdjacentHTML.
  • Remoção e substituição de elementos com remove() e replaceWith().

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:

  • innerHTML nos 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.
  • textContent define 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.
Informação

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>.
Informação

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 / false

toggle 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 por tagName. 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.
Nota

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 start

Clonando 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.
Informação

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 reflow

Duas 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:

  • createElement cria um novo elemento de item de lista para a tarefa, e appendChild o 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 inspeciona event.target para 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.
Nota

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.

Informação

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:

Prática

Prática
Qual dos seguintes métodos pode ser usado para alterar o conteúdo de um elemento do DOM?
Qual dos seguintes métodos pode ser usado para alterar o conteúdo de um elemento do DOM?
Prática
Você precisa exibir com segurança um comentário digitado por um usuário dentro de um parágrafo. A qual propriedade você deve atribuí-lo?
Você precisa exibir com segurança um comentário digitado por um usuário dentro de um parágrafo. A qual propriedade você deve atribuí-lo?
Prática
Qual é a principal vantagem de inserir muitos elementos novos por meio de um DocumentFragment?
Qual é a principal vantagem de inserir muitos elementos novos por meio de um DocumentFragment?
Was this page helpful?