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

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
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
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
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.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *