Cómo construir tu primer gestor de proyectos en JavaScript y no morir en el intento
Introducción
Todos los que empezamos en el mundo de la programación hemos pasado por lo mismo: nos llena la emoción, abrimos el editor de código, comenzamos tres proyectos al mismo tiempo y, dos semanas después, ninguno está terminado. La frustración aparece, sentimos que no avanzamos y dan ganas de tirar la toalla.
En la comunidad de desarrollo web, esta experiencia es extremadamente común. Intentar construir la versión perfecta de una idea desde el día uno suele llevar al estancamiento. La clave para superar este obstáculo no es trabajar más horas, sino reducir el alcance de tus proyectos y avanzar a pasos pequeños pero constantes.
En este tutorial práctico para principiantes, aprenderemos a construir una aplicación web funcional de seguimiento de ideas y proyectos utilizando HTML5, CSS3 y JavaScript básico. Además, utilizaremos localStorage para que la información no se borre al recargar la página. Este proyecto no solo reforzará tus bases en JavaScript, sino que te dará una herramienta real para organizar tus futuros avances como desarrollador.
Explicación paso a paso: Planificando la aplicación
Para evitar abrumarnos, definiremos exactamente qué debe hacer nuestra aplicación antes de escribir una sola línea de código:
- Capturar datos: Un formulario sencillo donde podamos ingresar el nombre del proyecto y su estado (por ejemplo: “En idea”, “En proceso” o “Completado”).
- Visualizar el listado: Una sección que muestre las tarjetas de cada proyecto.
- Persistir la información: Guardar los datos en el navegador usando
localStoragepara que la lista siga ahí cuando cierres la pestaña. - Eliminar registros: Un botón para borrar proyectos finalizados o descartados.
Al mantener la arquitectura simple, nos enfocamos en dominar la manipulación del DOM (Document Object Model) y los eventos en JavaScript.
Código funcional
A continuación, crearemos tres archivos principales para mantener una estructura organizada y limpia.
1. El documento HTML (index.html)
Aquí creamos la estructura base con un formulario y un contenedor para las tarjetas.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi Gestor de Proyectos - 8devmx</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="container">
<h1>Mi Gestor de Proyectos</h1>
<p class="subtitle">Registra tus ideas y evita el bloqueo del desarrollador.</p>
<form id="project-form" class="card">
<div class="form-group">
<label for="title">Nombre del proyecto:</label>
<input type="text" id="title" placeholder="Ej. Portafolio personal" required>
</div>
<div class="form-group">
<label for="status">Estado actual:</label>
<select id="status">
<option value="En idea">💡 En idea</option>
<option value="En proceso">⚡ En proceso</option>
<option value="Completado">✅ Completado</option>
</select>
</div>
<button type="submit" class="btn-primary">Guardar Proyecto</button>
</form>
<section class="list-section">
<h2>Proyectos Registrados</h2>
<div id="projects-list" class="grid-container">
<!-- Las tarjetas se insertarán dinámicamente con JS -->
</div>
</section>
</main>
<script src="app.js"></script>
</body>
</html>
2. Los estilos CSS (styles.css)
Aplicamos estilos limpios y modernos usando Flexbox y CSS Grid.
:root {
--bg-color: #0f172a;
--card-bg: #1e293b;
--accent: #38bdf8;
--text-color: #f8fafc;
--border-color: #334155;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
padding: 2rem 1rem;
}
.container {
max-width: 800px;
margin: 0 auto;
}
h1 {
color: var(--accent);
margin-bottom: 0.5rem;
}
.subtitle {
margin-bottom: 2rem;
opacity: 0.8;
}
.card {
background-color: var(--card-bg);
padding: 1.5rem;
border-radius: 8px;
border: 1px solid var(--border-color);
margin-bottom: 2rem;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
}
input, select {
width: 100%;
padding: 0.75rem;
border-radius: 4px;
border: 1px solid var(--border-color);
background-color: var(--bg-color);
color: var(--text-color);
}
.btn-primary {
background-color: var(--accent);
color: #0f172a;
font-weight: bold;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
.btn-primary:hover {
opacity: 0.9;
}
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.project-card {
background-color: var(--card-bg);
padding: 1rem;
border-radius: 6px;
border-left: 4px solid var(--accent);
display: flex;
justify-content: space-between;
align-items: center;
}
.btn-delete {
background-color: #ef4444;
color: white;
border: none;
padding: 0.4rem 0.8rem;
border-radius: 4px;
cursor: pointer;
}
3. La lógica en JavaScript (app.js)
Aquí escuchamos eventos, interactuamos con el DOM y manejamos la persistencia de datos.
// Selección de elementos del DOM
const projectForm = document.getElementById('project-form');
const titleInput = document.getElementById('title');
const statusSelect = document.getElementById('status');
const projectsList = document.getElementById('projects-list');
// Cargar proyectos guardados al iniciar la página
document.addEventListener('DOMContentLoaded', displayProjects);
// Escuchar el evento de envío del formulario
projectForm.addEventListener('submit', function(event) {
event.preventDefault(); // Evita que la página se recargue
const title = titleInput.value.trim();
const status = statusSelect.value;
if (title === '') return;
const newProject = {
id: Date.now(),
title: title,
status: status
};
saveProject(newProject);
displayProjects();
projectForm.reset();
});
// Función para obtener proyectos de localStorage
function getProjects() {
const stored = localStorage.getItem('my_projects');
return stored ? JSON.parse(stored) : [];
}
// Función para guardar un nuevo proyecto
function saveProject(project) {
const projects = getProjects();
projects.push(project);
localStorage.setItem('my_projects', JSON.stringify(projects));
}
// Función para renderizar las tarjetas en pantalla
function displayProjects() {
const projects = getProjects();
projectsList.innerHTML = '';
if (projects.length === 0) {
projectsList.innerHTML = '<p>No tienes proyectos registrados aún.</p>';
return;
}
projects.forEach(project => {
const card = document.createElement('div');
card.className = 'project-card';
card.innerHTML = `
<div>
<h3>${project.title}</h3>
<small>${project.status}</small>
</div>
<button class="btn-delete" onclick="deleteProject(${project.id})">Eliminar</button>
`;
projectsList.appendChild(card);
});
}
// Función para eliminar un proyecto por su ID
function deleteProject(id) {
let projects = getProjects();
projects = projects.filter(project => project.id !== id);
localStorage.setItem('my_projects', JSON.stringify(projects));
displayProjects();
}
Errores comunes al empezar un proyecto web
- Queremos abarcar demasiado desde el inicio: Intentar integrar bases de datos complejas, marcos de trabajo (como React) y pasarelas de pago antes de dominar los fundamentos produce frustración rápidamente.
- Olvidar convertir objetos con
JSON.stringify:localStoragesolo almacena cadenas de texto. Si intentas guardar un objeto o arreglo directamente, se guardará como"[object Object]". - No prevenir la recarga del formulario: Si olvidas colocar
event.preventDefault(), el formulario enviará los datos mediante una recarga HTTP tradicional y la ejecución de tu código JS se interrumpe.
Buenas prácticas recomendadas
- Divide tus problemas en partes pequeñas: Programa primero el HTML, asegúrate de que se vea bien con CSS y al final agrega la lógica con JS.
- Variables semánticas: Usa nombres descriptivos para tus variables (
projectsList,saveProject) en lugar de letras sueltas. - Aprovecha
DOMContentLoaded: Asegúrate de ejecutar tu código JavaScript una vez que el árbol HTML esté totalmente cargado por el navegador.
Ejercicio práctico
Para poner a prueba lo aprendido, intenta agregar una pequeña mejora por tu cuenta:
- Reto: Modifica el CSS y el JavaScript para que las tarjetas cambien de color según su estado (por ejemplo, verde para “Completado” y amarillo para “En idea”).
Pista: Puedes agregar una clase CSS condicional dentro de la función displayProjects() revisando el valor de project.status.
Fuente original
Este contenido está inspirado en reflexiones de la comunidad sobre la frustración al iniciar proyectos y cómo superarla construyendo prototipos simples. Puedes consultar la publicación original en inglés Nothing happened.