javascript

Lista de tareas pendientes con JavaScript

Hoy te mostraré cómo hacer una lista de tareas pendientes con JavaScript puro, sin frameworks. A esto también se le conoce como To Do list.

Este proyecto de lista de pendientes es un proyecto básico para comenzar en este mundo de la programación web.

Solo usaremos JavaScript, arreglos, acceso a los elementos del DOM, eventos y almacenamiento con localstorage.

Lo que tendremos al final será una lista de tareas. A cada tarea podremos marcarla como completada o eliminarla, y también agregar una nueva tarea. Todo será guardado en LocalStorage dentro del navegador web.

Explicación general

Vamos a tener toda la lista de tareas en un arreglo. El arreglo estará lleno de objetos que van a tener la propiedad tarea y completada.

Ese arreglo será codificado como JSON y guardado en localstorage. Cada que el usuario modifique una tarea se modificará el arreglo en sí y después se guardará. Al inicio de la aplicación el arreglo se va a recuperar de localStorage.

Por cierto, usaremos las funciones push y splice para trabajar con el arreglo.

Diseñando página con HTML

Primero veamos la página. Contamos con un input que será para agregar una nueva tarea a nuestra To-Do list con JavaScript, además de una lista que será la contenedora de todos los elementos.

Todos los elementos tienen un id para que más tarde los recuperemos desde JavaScript usando querySelector.

<!DOCTYPE html>
<html lang="es">

<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Lista de tareas con JS - By Parzibyte</title>
 <link rel="stylesheet" href="estilo.css">
</head>

<body>
 <h2>Lista de tareas pendientes</h2>
 <label for="inputNuevaTarea">Agregar nueva tarea: </label>
 <input type="text" id="inputNuevaTarea">
 <button id="btnAgregarTarea">Ok</button>
 <ul id="contenedorTareas"></ul>
 <a href="https://parzibyte.me/blog">By Parzibyte</a>
</body>
<script src="script.js"></script>

</html>

Por el momento fíjate en inputNuevaTarea, btnAgregarTarea y contenedorTareas que vamos a recuperar más tarde desde JS:

 const $contenedorTareas = document.querySelector("#contenedorTareas"),
  $btnGuardarTarea = document.querySelector("#btnAgregarTarea"),
  $inputNuevaTarea = document.querySelector("#inputNuevaTarea");

Estilo CSS

Estilo CSS para marcar tarea completada – Lista de tareas pendientes con JavaScript

Esta lista debe tener ciertos estilos. Lo único que he agregado es la separación así como el texto tachado para agregar esa clase a los elementos completados:


 li {
  list-style: none;
  margin-bottom: 5px;
 }

 a.enlace-eliminar {
  margin-left: 10px;
  text-decoration: none;
  font-size: 1rem;
 }

 .tachado {
  text-decoration: line-through;
 }

Lo del texto tachado se consigue colocando text-decoration en line-throug.

Guardar y recuperar lista de localstorage

Lista de tareas (to do list) en localstorage con JS

Toda la lista va a vivir en un arreglo pero ese arreglo debe ser guardado de manera permanente para que las tareas pendientes sigan ahí incluso si se navega a otra página o se refresca la pestaña actual.

Lo único que hacemos es codificar y decodificar como JSON para después guardar en el almacenamiento del navegador.

const CLAVE_LOCALSTORAGE = "lista_tareas";
const obtenerTareasDeAlmacenamiento = () => {
  const posibleLista = JSON.parse(localStorage.getItem(CLAVE_LOCALSTORAGE));
  if (posibleLista) {
    return posibleLista;
  } else {
    return [];
  }
};

const guardarTareasEnAlmacenamiento = () => {
  localStorage.setItem(CLAVE_LOCALSTORAGE, JSON.stringify(tareas));
};

En este caso la variable tareas será la lista, y como es una variable global no necesitamos que nos la pasen como argumento.

Agregar tarea

Vamos a escuchar el clic del botón. Cuando pase eso, recuperamos el valor del input y agregamos una nueva tarea en caso de que no sea una cadena vacía. Por defecto la tarea no está terminada.

// Escuchar clic del botón para agregar nueva tarea
$btnGuardarTarea.onclick = () => {
  const tarea = $inputNuevaTarea.value;
  if (!tarea) {
    return;
  }
  tareas.push({
    tarea: tarea,
    terminada: false,
  });
  $inputNuevaTarea.value = "";
  guardarTareasEnAlmacenamiento();
  refrescarListaDeTareas();
};

Estoy invocando a guardarTareasEnAlmacenamiento (que ya expliqué anteriormente) y después a refrescarListaDeTareas, que vamos a ver a continuación.

Obtener lista de tareas pendientes con JavaScript

Lo que falta es la función más importante: la de recorrer el arreglo de tareas y dibujar toda la lista en el DOM. Es en esta función en donde escuchamos el evento change del input checkbox y marcamos a la tarea como terminada o como no terminada.

También es aquí donde escuchamos el clic del botón para eliminar una tarea, en donde simplemente modificamos el arreglo y guardamos las tareas en el almacenamiento. Es un poco larga pero queda así:

// Definir función que refresca la lista de tareas a partir del arreglo global
const refrescarListaDeTareas = () => {
  $contenedorTareas.innerHTML = "";
  for (const [indice, tarea] of tareas.entries()) {
    // Crear el enlace para eliminar la tarea
    const $enlaceParaEliminar = document.createElement("a");
    $enlaceParaEliminar.classList.add("enlace-eliminar");
    $enlaceParaEliminar.innerHTML = "&times;";
    $enlaceParaEliminar.href = "";
    $enlaceParaEliminar.onclick = (evento) => {
      evento.preventDefault();
      if (!confirm("¿Eliminar tarea?")) {
        return;
      }
      tareas.splice(indice, 1);
      // Guardar los cambios
      guardarTareasEnAlmacenamiento(tareas);
      refrescarListaDeTareas();
    };
    // El input para marcar la tarea como terminada
    const $checkbox = document.createElement("input");
    $checkbox.type = "checkbox";
    $checkbox.onchange = function () { // No es una función flecha porque quiero acceder al elemento a través de this
      if (this.checked) {
        tareas[indice].terminada = true;
      } else {
        tareas[indice].terminada = false;
      }
      guardarTareasEnAlmacenamiento(tareas);
      refrescarListaDeTareas();
    }

    // El span que llevará el contenido de la tarea
    const $span = document.createElement("span");
    $span.textContent = tarea.tarea;
    // Y finalmente el elemento de la lista
    const $li = document.createElement("li");
    // Verificamos si la tarea está marcada para marcar los elementos
    if (tarea.terminada) {
      $checkbox.checked = true;
      $span.classList.add("tachado");
    }
    $li.appendChild($checkbox);
    $li.appendChild($span);
    $li.appendChild($enlaceParaEliminar);
    $contenedorTareas.appendChild($li);
  }
};

En este caso estoy haciendo un for of para recorrer el arreglo por índice y valor. Me gusta más esta versión que la de usar un forEach.

Ya para terminar obtenemos todas las tareas y refrescamos la lista al inicio de la aplicación:

 // Llamar a la función la primera vez
 tareas = obtenerTareasDeAlmacenamiento();
 refrescarListaDeTareas();

Poniendo todo junto

Lista de tareas pendientes con JavaScript

El código completo de JavaScript queda así:

const CLAVE_LOCALSTORAGE = "lista_tareas";
document.addEventListener("DOMContentLoaded", () => {
 let tareas = []; // El arreglo global que vamos a manejar
 // Declaración de elementos del DOM
 const $contenedorTareas = document.querySelector("#contenedorTareas"),
  $btnGuardarTarea = document.querySelector("#btnAgregarTarea"),
  $inputNuevaTarea = document.querySelector("#inputNuevaTarea");

 // Escuchar clic del botón para agregar nueva tarea
 $btnGuardarTarea.onclick = () => {
  const tarea = $inputNuevaTarea.value;
  if (!tarea) {
   return;
  }
  tareas.push({
   tarea: tarea,
   terminada: false,
  });
  $inputNuevaTarea.value = "";
  guardarTareasEnAlmacenamiento();
  refrescarListaDeTareas();
 };

 const obtenerTareasDeAlmacenamiento = () => {
  const posibleLista = JSON.parse(localStorage.getItem(CLAVE_LOCALSTORAGE));
  if (posibleLista) {
   return posibleLista;
  } else {
   return [];
  }
 };

 const guardarTareasEnAlmacenamiento = () => {
  localStorage.setItem(CLAVE_LOCALSTORAGE, JSON.stringify(tareas));
 };

 // Definir función que refresca la lista de tareas a partir del arreglo global
 const refrescarListaDeTareas = () => {
  $contenedorTareas.innerHTML = "";
  for (const [indice, tarea] of tareas.entries()) {
   // Crear el enlace para eliminar la tarea
   const $enlaceParaEliminar = document.createElement("a");
   $enlaceParaEliminar.classList.add("enlace-eliminar");
   $enlaceParaEliminar.innerHTML = "&times;";
   $enlaceParaEliminar.href = "";
   $enlaceParaEliminar.onclick = (evento) => {
    evento.preventDefault();
    if (!confirm("¿Eliminar tarea?")) {
     return;
    }
    tareas.splice(indice, 1);
    // Guardar los cambios
    guardarTareasEnAlmacenamiento(tareas);
    refrescarListaDeTareas();
   };
   // El input para marcar la tarea como terminada
   const $checkbox = document.createElement("input");
   $checkbox.type = "checkbox";
   $checkbox.onchange = function () { // No es una función flecha porque quiero acceder al elemento a través de this
    if (this.checked) {
     tareas[indice].terminada = true;
    } else {
     tareas[indice].terminada = false;
    }
    guardarTareasEnAlmacenamiento(tareas);
    refrescarListaDeTareas();
   }

   // El span que llevará el contenido de la tarea
   const $span = document.createElement("span");
   $span.textContent = tarea.tarea;
   // Y finalmente el elemento de la lista
   const $li = document.createElement("li");
   // Verificamos si la tarea está marcada para marcar los elementos
   if (tarea.terminada) {
    $checkbox.checked = true;
    $span.classList.add("tachado");
   }
   $li.appendChild($checkbox);
   $li.appendChild($span);
   $li.appendChild($enlaceParaEliminar);
   $contenedorTareas.appendChild($li);
  }
 };
 // Llamar a la función la primera vez
 tareas = obtenerTareasDeAlmacenamiento();
 refrescarListaDeTareas();
});

Si quieres puedes acceder al código completo aquí. Por otro lado, te dejo una demostración en este enlace y más contenido sobre JavaScript en este link.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.