JavaScript: Projeto Final: App de Tarefas
Parabéns por chegar à última lição! Ao longo das 27 lições anteriores, você aprendeu sobre variáveis, funções, arrays, DOM, eventos, JSON, programação assíncrona, classes e muito mais. Agora é hora de juntar tudo e construir uma aplicação pequena completa. Este projeto será a consolidação de tudo o que você aprendeu.
1. Visão do Projeto
Vamos construir um App de Tarefas — pode parecer simples, mas toca quase todos os conceitos que você estudou. Quando terminar, você terá uma ferramenta de produtividade real e utilizável.
▶ Exemplo
// Exemplo de código desta seção
const message = "Olá, Mundo!";
console.log(message);
Este é o exemplo mais simples; as seções seguintes explicarão em detalhes.## 2. Requisitos Funcionais
- Adicionar itens de tarefa — campo de input + botão; pressionar Enter ou clicar adiciona um novo item
- Alternar concluída/pendente — clicar em uma tarefa alterna seu estado (efeito de riscado)
- Excluir itens de tarefa — cada item tem um botão de exclusão ao lado
- Filtragem — três botões: Todas / Pendentes / Concluídas
- Armazenamento local — use
localStoragepara persistir dados entre atualizações da página - Contar itens restantes — exiba "X itens restantes" na parte inferior
3. Requisitos Técnicos
| Conceito | Onde é Usado |
|---|---|
| Classe | Classe AppTarefas organiza toda a lógica |
| async/await | Envolver leitura/escrita do localStorage como funções assíncronas |
| Manipulação do DOM | Criar itens de lista dinamicamente, atualizar conteúdo, remover elementos |
| Tratamento de eventos | Formulário submit, lista clique, botão de filtro clique |
| JSON | JSON.stringify para salvar, JSON.parse para carregar |
| Métodos de array | filter, map, find para gerenciar dados de tarefas |
| Template literals | Construir strings HTML |
4. Critérios de Aceitação
- [ ] Todas as 6 funcionalidades funcionam corretamente
- [ ] Dados persistem após atualização da página
- [ ] Código tem comentários claros
- [ ] Sem
alert/prompt/confirm— use interações na página - [ ] Input vazio é rejeitado (com mensagem)
5. Ideias de Implementação de Referência
Abaixo estão dicas e direções — não código completo. Escrever você mesmo é onde o aprendizado real acontece.
(1) Estrutura de Dados
<script>
// Cada item de tarefa é um objeto
const tarefa = {
id: Date.now(), // identificador único
texto: 'Aprender JavaScript',
concluida: false
};
console.log(tarefa);
// Todas as tarefas são armazenadas em um array
let tarefas = [];
</script>
(2) Estrutura da Classe
<script>
class AppTarefas {
constructor() {
this.tarefas = []; // dados
this.filtro = 'todas'; // filtro atual
this.init(); // inicializar
}
async init() {
// 1. Carregar dados do localStorage
// 2. Vincular eventos
// 3. Renderizar a lista
}
async salvar() {
// JSON.stringify → localStorage.setItem
}
async carregar() {
// localStorage.getItem → JSON.parse
}
adicionarTarefa(texto) { /* criar objeto, push, salvar, renderizar */ }
alternarTarefa(id) { /* encontrar item, inverter concluida, salvar, renderizar */ }
excluirTarefa(id) { /* filtrar item, salvar, renderizar */ }
renderizar() {
// 1. Filtrar tarefas com base neste.filtro
// 2. Limpar o container da lista
// 3. Percorrer array filtrado, criar nós DOM
// 4. Atualizar exibição da contagem
}
}
</script>
(3) Estratégia de Vinculação de Eventos
- Formulário
submit:e.preventDefault()→ obter valor do input →adicionarTarefa(texto)→ limpar input - Lista
clique(delegação de eventos): verificar see.targeté o botão de alternar ou excluir → chamar o método apropriado - Botão de filtro
clique: atualizareste.filtro→renderizar()
(4) Estratégia de Renderização
<script>
renderizar() {
const filtradas = this.tarefas.filter(function(tarefa) {
if (this.filtro === 'pendentes') return !tarefa.concluida;
if (this.filtro === 'concluidas') return tarefa.concluida;
return true; // 'todas'
}.bind(this));
// Limpar o container
this.listaEl.innerHTML = '';
filtradas.forEach(function(tarefa) {
var li = document.createElement('li');
// Definir conteúdo, estilos, atributos de dados
// Vincular eventos de alternar e excluir
this.listaEl.appendChild(li);
}.bind(this));
// Atualizar a contagem
var contagemPendentes = this.tarefas.filter(function(t) { return !t.concluida; }).length;
this.contagemEl.textContent = contagemPendentes + ' itens restantes';
}
</script>
6. Ideias de Extensão
Quando as funcionalidades básicas estiverem funcionando, tente estes desafios avançados:
- 🔄 Ordenação por arrastar — implemente com a API HTML5 Drag & Drop ou eventos de toque
- ✏️ Edição — clique duplo em uma tarefa para entrar no modo edição, pressione Enter para salvar
- 🏷️ Etiquetas de categoria — adicione rótulos (trabalho / estudo / vida) e filtre por etiqueta
- 🎨 Troca de tema — tema claro/escuro, persistido com
localStorage - ⏰ Lembretes de prazo — adicione um prazo a cada item, destaque em vermelho os atrasados
7. Tarefa
Complete o app de tarefas acima com todas as funcionalidades, e passe em todos os critérios de aceitação.
Passos sugeridos:
- Comece com a estrutura HTML e estilização CSS
- Construa o esqueleto da classe
AppTarefas(constructor,init) - Implemente
adicionarTarefaerenderizar— faça a adição funcionar primeiro - Implemente
alternarTarefa— alternar concluída/pendente - Implemente
excluirTarefa— remover itens - Implemente filtragem
- Implemente
salvarecarregar— persistir dados - Mostre a contagem de itens restantes
- Acabamento final: validação de input vazio, ajustes de estilo
▶ Exemplo
Um app Todo completo usando classes e localStorage:
<!DOCTYPE html>
<html>
<head><style>body{font-family:Arial;max-width:600px;margin:20px auto;}.done{text-decoration:line-through;color:gray;}</style></head>
<body>
<h1>Todo App</h1>
<input id="taskInput" placeholder="Nova tarefa">
<button id="addBtn">Adicionar</button>
<ul id="taskList"></ul>
<script>
class Todo {
constructor() { this.tasks = JSON.parse(localStorage.getItem('tasks') || '[]'); }
add(text) { this.tasks.push({text, done: false}); localStorage.setItem('tasks', JSON.stringify(this.tasks)); this.render(); }
toggle(i) { this.tasks[i].done = !this.tasks[i].done; localStorage.setItem('tasks', JSON.stringify(this.tasks)); this.render(); }
render() { document.getElementById('taskList').innerHTML = this.tasks.map((t, i) => `<li class="${t.done ? 'done' : ''}">${t.text}</li>`).join(''); }
}
const todo = new Todo();
document.getElementById('addBtn').onclick = () => todo.add(document.getElementById('taskInput').value);
todo.render();
</script>
</body>
</html>
Demonstra um app Todo completo com gerenciamento de estado baseado em classes, persistência com localStorage e renderização dinâmica de DOM.
❓ Perguntas Frequentes
P: Por que usar Class em vez de função construtora? R: A sintaxe Class é mais limpa —
constructor,thise herança têm sintaxe clara. Por baixo dos panos ainda são protótipos, mas lê como linguagens OOP e é mais fácil de manter. P: Os dados do localStorage podem ser perdidos? R: Não, o localStorage persiste até que o usuário limpe os dados do navegador ou use navegação privada. Lembre-se: só armazena strings — objetos precisam deJSON.stringifyantes de salvar. P: O que é delegação de eventos e por que é útil em um app de tarefas? R: Delegação de eventos significa vincular um listener ao elemento pai e usare.targetpara manipular eventos dos filhos. Novas tarefas ganham tratamento de evento automaticamente — melhor performance.
📖 Resumo
- Este projeto integra variáveis, funções, arrays, objetos, manipulação DOM, eventos, JSON e Class
- Usar Class organiza o código —
constructorinicializa, métodos ficam no prototype - localStorage persiste dados entre recarregamentos de página
- Delegação de eventos aproveita o bubbling para reduzir bindings duplicados
- Filtragem funciona mantendo um estado
filtere renderização condicional — base do padrão MVVM
📝 Exercícios
- Básico: Complete as funcionalidades principais de tarefas — adicionar, alternar concluída, excluir — usando um array para armazenamento (sem precisar de
localStorage). - Intermediário: Adicione persistência com
localStoragee filtragem (Todas / Pendentes / Concluídas) à versão básica; os dados devem sobreviver a uma atualização da página. - Desafio: Implemente pelo menos uma funcionalidade de extensão (arrastar para ordenar, edição, etiquetas de categoria, troca de tema ou lembretes de prazo), e escreva uma breve explicação da sua abordagem.