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:
- Prioridade — adicione um seletor
<select>com opções baixa, média e alta. Exiba uma bolinha colorida ao lado de cada tarefa - Edição — ao dar duplo clique no texto, transforme-o em um
<input>editável. Ao pressionarEnterou perder o foco, salve a edição - Filtro — adicione botões para mostrar: Todas / Pendentes / Concluídas
Conclusão
Neste artigo você aprendeu:
- Criar elementos com
createElemente configurá-los antes de inserir - Inserir elementos com
appendChild,append,prepend,before,after - Inserir HTML com
insertAdjacentHTML - Remover elementos com
removeeremoveChild - 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
- MDN Web Docs — Document.createElement: https://developer.mozilla.org/pt-BR/docs/Web/API/Document/createElement
- MDN Web Docs — Node.appendChild: https://developer.mozilla.org/pt-BR/docs/Web/API/Node/appendChild
- MDN Web Docs — Element.insertAdjacentHTML: https://developer.mozilla.org/pt-BR/docs/Web/API/Element/insertAdjacentHTML
- MDN Web Docs — DocumentFragment: https://developer.mozilla.org/pt-BR/docs/Web/API/DocumentFragment
- MDN Web Docs — ChildNode.remove: https://developer.mozilla.org/pt-BR/docs/Web/API/ChildNode/remove
- JavaScript.info — Modifying the Document: https://javascript.info/modifying-document
- Eloquent JavaScript, Cap. 14 — The Document Object Model: https://eloquentjavascript.net/14_dom.html
- JavaScript & JQuery — Jon Duckett (Alta Books)