Ejercicio: Proyecto: lista de tareas
Objetivo
Construir la aplicación de tareas completa. Las pruebas ejecutan tu código sobre la página dada: simulan el uso real (añadir, marcar, borrar, filtrar, recargar) y comprueban el resultado.
La página de partida
Esta es la página sobre la que trabaja tu código. No la modifiques: escribe solo el JavaScript.
<main class="app">
<h1>Mis tareas</h1>
<form id="form-tarea">
<label for="texto">Nueva tarea</label>
<input id="texto" type="text" autocomplete="off">
<button type="submit">Añadir</button>
</form>
<p id="error" role="alert"></p>
<div class="filtros" role="group" aria-label="Filtrar tareas">
<button type="button" data-filtro="todas" aria-pressed="true">Todas</button>
<button type="button" data-filtro="pendientes" aria-pressed="false">Pendientes</button>
<button type="button" data-filtro="hechas" aria-pressed="false">Hechas</button>
</div>
<ul id="lista"></ul>
<p id="contador" role="status"></p>
<button type="button" id="borrar-hechas">Borrar completadas</button>
</main>
Pasos
Marca cada paso a medida que lo completes. Tu avance se guarda en este navegador.
💡 Pista 1: no sé por dónde empezar
Empieza solo por cargar, guardar, crearElemento y pintar, y prueba en el editor con unas tareas a mano. Cuando se vean bien, añade los eventos uno a uno.
💡 Pista 2: ids de tipo texto
li.dataset.id es texto y tu id guardado es número. Compara con Number(li.dataset.id) o guarda el id como texto en ambos lados.
🔎 Comprueba tu trabajo
Pega tu código y pulsa Comprobar. La revisión ocurre en tu navegador; nada se envía a ningún sitio. Cada punto en rojo incluye una pista.
✅ Ver una solución completa de ejemplo
Intenta resolverlo antes de mirar. Tu versión puede ser distinta y también correcta.
const CLAVE = "tareas";
let tareas = [];
let filtro = "todas";
const form = document.getElementById("form-tarea");
const campo = document.getElementById("texto");
const error = document.getElementById("error");
const lista = document.getElementById("lista");
const contador = document.getElementById("contador");
// ----- Almacenamiento -----
function cargar() {
try {
return JSON.parse(localStorage.getItem(CLAVE)) ?? [];
} catch {
return [];
}
}
function guardar() {
localStorage.setItem(CLAVE, JSON.stringify(tareas));
}
// ----- Pintar la página a partir de los datos -----
function visibles() {
if (filtro === "pendientes") return tareas.filter((t) => !t.hecha);
if (filtro === "hechas") return tareas.filter((t) => t.hecha);
return tareas;
}
function crearElemento(tarea) {
const li = document.createElement("li");
li.dataset.id = tarea.id;
li.classList.toggle("hecha", tarea.hecha);
const check = document.createElement("input");
check.type = "checkbox";
check.className = "check";
check.checked = tarea.hecha;
check.setAttribute("aria-label", "Marcar como hecha");
const texto = document.createElement("span");
texto.className = "texto";
texto.textContent = tarea.texto; // textContent: seguro con cualquier texto
const borrar = document.createElement("button");
borrar.type = "button";
borrar.className = "borrar";
borrar.setAttribute("aria-label", "Borrar tarea");
borrar.textContent = "✕";
li.append(check, texto, borrar);
return li;
}
function pintar() {
lista.replaceChildren(...visibles().map(crearElemento));
const pendientes = tareas.filter((t) => !t.hecha).length;
contador.textContent = pendientes === 1 ? "1 pendiente" : `${pendientes} pendientes`;
document.querySelectorAll("[data-filtro]").forEach((b) => {
b.setAttribute("aria-pressed", String(b.dataset.filtro === filtro));
});
}
function cambiar() { // cada cambio: guardar y volver a pintar
guardar();
pintar();
}
// ----- Eventos -----
form.addEventListener("submit", (e) => {
e.preventDefault();
const texto = campo.value.trim();
if (texto === "") {
error.textContent = "Escribe una tarea";
return;
}
error.textContent = "";
tareas.push({ id: Date.now() + Math.random(), texto, hecha: false });
campo.value = "";
cambiar();
});
lista.addEventListener("click", (e) => {
const li = e.target.closest("li");
if (!li) return;
const id = Number(li.dataset.id);
const tarea = tareas.find((t) => t.id === id);
if (!tarea) return;
if (e.target.classList.contains("check")) {
tarea.hecha = e.target.checked;
cambiar();
} else if (e.target.classList.contains("borrar")) {
tareas = tareas.filter((t) => t.id !== id);
cambiar();
}
});
document.querySelector(".filtros").addEventListener("click", (e) => {
const boton = e.target.closest("[data-filtro]");
if (!boton) return;
filtro = boton.dataset.filtro;
pintar();
});
document.getElementById("borrar-hechas").addEventListener("click", () => {
tareas = tareas.filter((t) => !t.hecha);
cambiar();
});
// ----- Arranque -----
function iniciar() {
tareas = cargar();
filtro = "todas";
pintar();
}
iniciar();
💡 🏁 Reto extra (opcional)
Añade edición: al hacer doble clic sobre el texto de una tarea, conviértelo en un <input>; al pulsar Enter se guarda y con Escape se cancela. Pruébalo con el teclado.