Javascript

Criando e Removendo Elementos Dinamicamente Já leu

10 min de leitura

Criando e Removendo Elementos Dinamicamente
Nos artigos anteriores aprendemos a selecionar elementos existentes no HTML e reagir a eventos. Mas em aplicações reais, boa parte da interface n&

Nos artigos anteriores aprendemos a selecionar elementos existentes no HTML e reagir a eventos. Mas em aplicações reais, boa parte da interface não existe no HTML quando a página carrega — ela é construída pelo JavaScript em tempo de execução.

Pense em uma lista de tarefas onde você adiciona itens, um feed de posts que carrega conforme você rola, uma tabela que é preenchida com dados de uma API. Tudo isso é criado dinamicamente. Este artigo ensina exatamente como fazer isso.


createElement — criando elementos do zero

O método document.createElement() cria um novo elemento HTML na memória — ele ainda não aparece na página:

// Cria um elemento <p> na memória
const paragrafo = document.createElement("p");

// Cria outros tipos de elementos
const titulo = document.createElement("h2");
const botao = document.createElement("button");
const imagem = document.createElement("img");
const input = document.createElement("input");
const lista = document.createElement("ul");
const item = document.createElement("li");

Criar o elemento não é suficiente — você precisa configurá-lo e depois inserir na página.


Configurando o elemento antes de inserir

const card = document.createElement("div");

// Conteúdo
card.textContent = "Meu primeiro card criado com JavaScript!";

// Classes
card.classList.add("card", "destaque");

// Atributos
card.setAttribute("id", "card-principal");
card.setAttribute("data-tipo", "informativo");

// Estilos inline (quando necessário)
card.style.padding = "1rem";
card.style.border = "1px solid #ddd";
card.style.borderRadius = "8px";

appendChild — inserindo na página

Após criar e configurar o elemento, use appendChild para adicioná-lo como último filho de um elemento pai:

const container = document.querySelector("#container");
const paragrafo = document.createElement("p");
paragrafo.textContent = "Este parágrafo foi criado com JavaScript.";

// Insere no final do container
container.appendChild(paragrafo);

Métodos modernos de inserção

O appendChild é clássico mas limitado. Os métodos modernos são mais flexíveis:

const lista = document.querySelector("#lista");
const novoItem = document.createElement("li");
novoItem.textContent = "Novo item";

// Insere como último filho (igual ao appendChild)
lista.append(novoItem);

// Insere como primeiro filho
lista.prepend(novoItem);

// Insere ANTES do elemento referenciado
const terceiroItem = document.querySelector("#item-3");
terceiroItem.before(novoItem);

// Insere DEPOIS do elemento referenciado
terceiroItem.after(novoItem);

// append também aceita strings diretamente
lista.append("Texto simples sem criar elemento");

// append aceita múltiplos elementos de uma vez
const item1 = document.createElement("li");
const item2 = document.createElement("li");
item1.textContent = "Item A";
item2.textContent = "Item B";
lista.append(item1, item2);

insertAdjacentHTML — inserindo HTML em string

Quando você precisa inserir HTML mais complexo rapidamente:

const container = document.querySelector("#container");

// Posições possíveis:
// "beforebegin" — antes do elemento
// "afterbegin"  — dentro, antes do primeiro filho
// "beforeend"   — dentro, depois do último filho
// "afterend"    — depois do elemento

container.insertAdjacentHTML("beforeend", `
  <div class="card">
    <h3>Título do Card</h3>
    <p>Descrição do card inserida via insertAdjacentHTML.</p>
    <button class="btn">Saiba mais</button>
  </div>
`);

Atenção: nunca use insertAdjacentHTML ou innerHTML com dados vindos diretamente do usuário. Use createElement + textContent nesses casos para evitar ataques XSS.


Removendo elementos

const item = document.querySelector("#item-remover");

// Forma moderna — o elemento remove a si mesmo
item.remove();

// Forma clássica — o pai remove o filho
const pai = item.parentElement;
pai.removeChild(item);

Limpando o conteúdo de um elemento

const lista = document.querySelector("#lista");

// ✅ Forma eficiente
lista.innerHTML = "";

// ✅ Alternativa com loop (útil quando precisa remover com lógica)
while (lista.firstChild) {
  lista.removeChild(lista.firstChild);
}

cloneNode — clonando elementos

const card = document.querySelector(".card-modelo");

// Clona apenas o elemento (sem filhos)
const clonaRaso = card.cloneNode(false);

// Clona o elemento e todos os filhos — deep clone
const clonaCompleto = card.cloneNode(true);

document.querySelector("#container").appendChild(clonaCompleto);

DocumentFragment — performance ao inserir muitos elementos

Quando você precisa inserir muitos elementos de uma vez, usar DocumentFragment é muito mais eficiente — você monta tudo na memória e insere de uma só vez, causando apenas um reflow (recálculo do layout):

const lista = document.querySelector("#lista");
const fragment = document.createDocumentFragment();

// Cria 100 itens na memória — zero operações no DOM real
for (let i = 1; i <= 100; i++) {
  const item = document.createElement("li");
  item.textContent = `Item ${i}`;
  fragment.appendChild(item);
}

// Uma única operação no DOM real
lista.appendChild(fragment);

Exemplo completo — To-Do List dinâmica

Vamos construir uma lista de tarefas completa com criação, conclusão e remoção de itens:

<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8">
  <title>To-Do List</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: 'Segoe UI', sans-serif;
      background: #f0f2f5;
      display: flex;
      justify-content: center;
      padding: 2rem 1rem;
    }

    .app {
      background: white;
      border-radius: 12px;
      padding: 2rem;
      width: 100%;
      max-width: 480px;
      box-shadow: 0 4px 24px rgba(0,0,0,.08);
    }

    h1 {
      font-size: 1.5rem;
      margin-bottom: 1.5rem;
      color: #1a1a2e;
    }

    .formulario {
      display: flex;
      gap: .5rem;
      margin-bottom: 1.5rem;
    }

    .formulario input {
      flex: 1;
      padding: .65rem 1rem;
      border: 2px solid #e0e0e0;
      border-radius: 8px;
      font-size: 1rem;
      transition: border-color .2s;
    }

    .formulario input:focus {
      outline: none;
      border-color: #5c6bc0;
    }

    .formulario button {
      padding: .65rem 1.2rem;
      background: #5c6bc0;
      color: white;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 1rem;
      font-weight: 600;
      transition: background .2s;
    }

    .formulario button:hover { background: #3949ab; }

    .info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: .85rem;
      color: #666;
      margin-bottom: 1rem;
    }

    .btn-limpar {
      background: none;
      border: none;
      color: #e53935;
      cursor: pointer;
      font-size: .85rem;
      text-decoration: underline;
    }

    .lista { list-style: none; }

    .tarefa {
      display: flex;
      align-items: center;
      gap: .75rem;
      padding: .75rem 1rem;
      border-radius: 8px;
      margin-bottom: .5rem;
      background: #f8f9ff;
      border: 1px solid #e8eaf6;
      transition: opacity .3s;
      animation: entrar .2s ease;
    }

    @keyframes entrar {
      from { opacity: 0; transform: translateY(-8px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    .tarefa.concluida { opacity: .5; }

    .tarefa.concluida .texto {
      text-decoration: line-through;
      color: #999;
    }

    .checkbox {
      width: 20px;
      height: 20px;
      cursor: pointer;
      accent-color: #5c6bc0;
    }

    .texto { flex: 1; font-size: .95rem; }

    .btn-remover {
      background: none;
      border: none;
      color: #ccc;
      cursor: pointer;
      font-size: 1.2rem;
      line-height: 1;
      transition: color .2s;
      padding: 0 .25rem;
    }

    .btn-remover:hover { color: #e53935; }

    .vazio {
      text-align: center;
      color: #bbb;
      padding: 2rem 0;
      font-size: .95rem;
    }
  </style>
</head>
<body>
  <div class="app">
    <h1>📝 Minhas Tarefas</h1>

    <div class="formulario">
      <input type="text" id="input-tarefa" placeholder="Nova tarefa..." maxlength="80">
      <button id="btn-adicionar">Adicionar</button>
    </div>

    <div class="info">
      <span id="contador">0 tarefas</span>
      <button class="btn-limpar" id="btn-limpar">Limpar concluídas</button>
    </div>

    <ul class="lista" id="lista"></ul>
  </div>

  <script>
    // Referências
    const inputTarefa = document.querySelector("#input-tarefa");
    const btnAdicionar = document.querySelector("#btn-adicionar");
    const btnLimpar = document.querySelector("#btn-limpar");
    const lista = document.querySelector("#lista");
    const contador = document.querySelector("#contador");

    // Estado
    let tarefas = [];
    let proximoId = 1;

    // ── Funções de renderização ──────────────────────

    function criarElementoTarefa(tarefa) {
      const li = document.createElement("li");
      li.classList.add("tarefa");
      if (tarefa.concluida) li.classList.add("concluida");
      li.dataset.id = tarefa.id;

      // Checkbox
      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";
      checkbox.classList.add("checkbox");
      checkbox.checked = tarefa.concluida;
      checkbox.addEventListener("change", () => alternarTarefa(tarefa.id));

      // Texto
      const span = document.createElement("span");
      span.classList.add("texto");
      span.textContent = tarefa.texto;

      // Botão remover
      const btnRemover = document.createElement("button");
      btnRemover.classList.add("btn-remover");
      btnRemover.textContent = "×";
      btnRemover.title = "Remover tarefa";
      btnRemover.addEventListener("click", () => removerTarefa(tarefa.id));

      li.append(checkbox, span, btnRemover);
      return li;
    }

    function renderizar() {
      lista.innerHTML = "";

      if (tarefas.length === 0) {
        const vazio = document.createElement("li");
        vazio.classList.add("vazio");
        vazio.textContent = "Nenhuma tarefa ainda. Adicione uma acima!";
        lista.appendChild(vazio);
        atualizarContador();
        return;
      }

      // Usa DocumentFragment para performance
      const fragment = document.createDocumentFragment();
      tarefas.forEach(tarefa => {
        fragment.appendChild(criarElementoTarefa(tarefa));
      });
      lista.appendChild(fragment);

      atualizarContador();
    }

    function atualizarContador() {
      const total = tarefas.length;
      const concluidas = tarefas.filter(t => t.concluida).length;
      const pendentes = total - concluidas;

      contador.textContent = total === 0
        ? "Nenhuma tarefa"
        : `${pendentes} pendente(s) · ${concluidas} concluída(s)`;
    }

    // ── Ações ────────────────────────────────────────

    function adicionarTarefa() {
      const texto = inputTarefa.value.trim();

      if (!texto) {
        inputTarefa.focus();
        inputTarefa.style.borderColor = "#e53935";
        setTimeout(() => {
          inputTarefa.style.borderColor = "";
        }, 1000);
        return;
      }

      const novaTarefa = {
        id: proximoId++,
        texto,
        concluida: false,
      };

      tarefas.push(novaTarefa);
      inputTarefa.value = "";
      inputTarefa.focus();
      renderizar();
    }

    function alternarTarefa(id) {
      tarefas = tarefas.map(t =>
        t.id === id ? { ...t, concluida: !t.concluida } : t
      );
      renderizar();
    }

    function removerTarefa(id) {
      // Animação de saída antes de remover
      const elemento = lista.querySelector(`[data-id="${id}"]`);
      if (elemento) {
        elemento.style.transition = "opacity .2s, transform .2s";
        elemento.style.opacity = "0";
        elemento.style.transform = "translateX(20px)";
        setTimeout(() => {
          tarefas = tarefas.filter(t => t.id !== id);
          renderizar();
        }, 200);
      }
    }

    function limparConcluidas() {
      tarefas = tarefas.filter(t => !t.concluida);
      renderizar();
    }

    // ── Eventos ──────────────────────────────────────

    btnAdicionar.addEventListener("click", adicionarTarefa);

    inputTarefa.addEventListener("keydown", (e) => {
      if (e.key === "Enter") adicionarTarefa();
    });

    btnLimpar.addEventListener("click", limparConcluidas);

    // ── Inicialização ─────────────────────────────────

    // Tarefas de exemplo para começar
    const tarefasIniciais = [
      "Estudar o Artigo 13 do curso",
      "Praticar criação de elementos no DOM",
      "Construir um projeto próprio",
    ];

    tarefasIniciais.forEach(texto => {
      tarefas.push({ id: proximoId++, texto, concluida: false });
    });

    renderizar();
    inputTarefa.focus();
  </script>
</body>
</html>

O que este projeto exercitou

Técnica Onde foi usada
createElement Criação de cada elemento da tarefa
appendChild / append Montagem do card da tarefa
remove + animação Remoção suave de tarefas
DocumentFragment Renderização performática da lista
dataset Armazenar o id da tarefa no elemento
classList.toggle Alternar estado de concluída
innerHTML = "" Limpar lista antes de re-renderizar
Delegação implícita Listeners criados junto com o elemento

Tarefa para você

Expanda a To-Do List com:

  1. Prioridade — adicione um seletor <select> com opções baixa, média e alta. Exiba uma bolinha colorida ao lado de cada tarefa
  2. Edição — ao dar duplo clique no texto, transforme-o em um <input> editável. Ao pressionar Enter ou perder o foco, salve a edição
  3. Filtro — adicione botões para mostrar: Todas / Pendentes / Concluídas

Conclusão

Neste artigo você aprendeu:

  • Criar elementos com createElement e configurá-los antes de inserir
  • Inserir elementos com appendChild, append, prepend, before, after
  • Inserir HTML com insertAdjacentHTML
  • Remover elementos com remove e removeChild
  • Clonar elementos com cloneNode
  • Otimizar inserções em massa com DocumentFragment
  • Construir uma To-Do List completa com estado, animações e boas práticas

No próximo artigo vamos aprender a trabalhar com formulários de forma profissional — coleta de dados, validação em tempo real e feedback visual para o usuário.


📌 Próximo artigo: Aula 14 — Formulários: validação e coleta de dados


📚 Fontes e Referências

Comentários

Mais em Javascript

O que é Programação Assíncrona? O Event Loop Explicado
O que é Programação Assíncrona? O Event Loop Explicado

Este &eacute; um dos artigos mais importantes de toda a s&eacute;rie. N&atild...

Mini Projeto: App de Clima Completo
Mini Projeto: App de Clima Completo

Chegamos ao fim do Módulo 3. Em oito artigos você percorreu um caminho que mu...

Testes automatizados com Jest
Testes automatizados com Jest

Existe um momento na vida de todo desenvolvedor em que ele mexe em um código...