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

LocalStorage e SessionStorage
LocalStorage e SessionStorage

Imagine que o usu&aacute;rio passou dez minutos preenchendo uma lista de tare...

Fetch API: consumindo dados da internet
Fetch API: consumindo dados da internet

At&eacute; agora simulamos requisi&ccedil;&otilde;es com&nbsp;setTimeout. A p...

Mini Projeto: Calculadora no Console
Mini Projeto: Calculadora no Console

Chegamos ao fim do primeiro m&oacute;dulo. Em nove artigos voc&ecirc; percorr...