Aprenda / JavaScript do zero

FundamentosAula 8 de 1622 min0% da trilha

Projeto guiado: lista de tarefas em JS puro

Junta DOM, eventos, array e async num único mini-app — sem framework nenhum.

Sem React, sem Next.js — só o que as 7 lições anteriores já cobriram. O objetivo é sentir, na mão, o trabalho manual que um framework resolve por você (e é exatamente o trabalho que a trilha de Frontend vai automatizar a partir da próxima trilha).

1. O estado — um array simples guarda tudo

let tarefas = []; // cada item: { id, titulo, feita }

function adicionarTarefa(titulo) {
  tarefas.push({ id: Date.now(), titulo, feita: false });
  renderizar();
}

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

function removerTarefa(id) {
  tarefas = tarefas.filter((t) => t.id !== id);
  renderizar();
}

Repare o padrão: nenhuma dessas funções mexe direto no DOM. Elas só atualizam o array tarefas e chamam renderizar() — quem sabe desenhar a tela é uma função só, separada da lógica de dado. Esse é o mesmo princípio que o React formaliza (estado muda → tela é redesenhada a partir do estado).

2. Renderizar — reconstruir o DOM a partir do array

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

function renderizar() {
  lista.innerHTML = ""; // limpa tudo — abordagem simples, um framework otimiza isso

  for (const tarefa of tarefas) {
    const item = document.createElement("li");
    item.textContent = tarefa.titulo;
    item.style.textDecoration = tarefa.feita ? "line-through" : "none";

    const botaoConcluir = document.createElement("button");
    botaoConcluir.textContent = tarefa.feita ? "Desfazer" : "Concluir";
    botaoConcluir.addEventListener("click", () => alternarFeita(tarefa.id));

    const botaoRemover = document.createElement("button");
    botaoRemover.textContent = "Remover";
    botaoRemover.addEventListener("click", () => removerTarefa(tarefa.id));

    item.append(botaoConcluir, botaoRemover);
    lista.appendChild(item);
  }
}

3. O formulário

const form = document.querySelector("#form-nova-tarefa");
const input = document.querySelector("#input-titulo");

form.addEventListener("submit", (evento) => {
  evento.preventDefault(); // impede o recarregamento padrão da página
  if (input.value.trim() === "") return;

  adicionarTarefa(input.value.trim());
  input.value = "";
});

4. Carregando do "servidor" (simulado com async)

async function carregarTarefasIniciais() {
  // simula uma API real, que levaria um tempo pra responder
  const resposta = await new Promise((resolve) =>
    setTimeout(() => resolve([{ id: 1, titulo: "Estudar JS", feita: false }]), 500),
  );
  tarefas = resposta;
  renderizar();
}

carregarTarefasIniciais();

Exercício

Adicione um contador que mostra "X de Y tarefas concluídas" acima da lista, atualizado toda vez que renderizar() roda.

Quiz rápido

Por que renderizar() fica separada das funções que mudam o array tarefas?
Pra que serve evento.preventDefault() no submit do formulário?