To ask a simple question; Do we need another JavaScript to-do list? I guess it depends on whom it’s for to guess the right answer. A to-do app is generally a beginner’s project in any programming language. If I chuck another one in the mix it may be useful. It is likely to be different from any other. Why is that? My father would say, “There is more than one way to skin a cat”. (Not that he would have, he loved cats). In programming, there are many ways to do things. Whether it’s a different naming convention, organisation of code. We all have our own ways to do things. Anyhow, enough of my ramblings, let’s begin.
Table of contents
Prerequisites
A modern browser and a text editor. I recommend VS Code and the Live Server extension. Some knowledge of HTML and JavaScript would be beneficial but not required. I am using a pre-compiled version of the Bulma CSS Framework in this vanilla JavaScript To-do list tutorial.
Download
Also available on GitHub
The Concept
If you are familiar with a to-do app you may skip to the next section. If you want to see the plan of action then look no more.
Here is our rough pseudocode:
- Page loads and displays items, if any.
- Enter text in the input.
- Hit enter or the save button.
- Item saves to local storage.
- The page refreshes
There is a main section of the code. There are 3 functions in total. Add and delete to-dos and a function that saves to localStorage.
Let’s Get Coding
First, we will start with the HTML. It’s a pretty basic template using the Bulma CSS framework. In a real-world scenario, it would be more beneficial to use SASS. This would compile what you need or use into 1 CSS file. I have added an input field, a save button and an empty container for the JavaScript to-do list inside the HTML file. It is possible to write them in JavaScript too.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link href="css/bulma.min.css" rel="stylesheet" />
<link href="css/style.css" rel="stylesheet" />
<title>Todo App</title>
</head>
<body>
<section class="section">
<div class="container">
<div id="app">
<h1 class="title">Worldoweb Todo</h1>
<div class="field has-addons">
<div class="control is-expanded">
<input class="input is-medium" id="todoInput" type="text" placeholder="What do you want TODO">
</div>
<div class="control">
<button id="add-btn" class="button is-medium is-primary">Save</button>
</div>
</div>
<div id="todoContainer">
<ul class="list" id="list"></ul>
</div>
</div>
</div>
</section>
<script src="js/global.js"></script>
</body>
</html>
Monitor Keyup Event
To start with, we will add an event listener to watch the enter key. Once the user presses enter, it will save to localStorage. There is also an event listener attached to the add button.
/**
* Monitors for keyup event
* @param {object} e The event object
*/
const keyup = (e) => {
let keyPressed = e.keyCode || e.which;
if (keyPressed === 13) {
addTodo(input.value)
}
};
document.addEventListener('keyup', keyup)
/**
* Attaches an event listener to the button
*/
document.getElementById("add-btn").addEventListener("click", () => {
addTodo(input.value);
});
Here comes the 3 functions deleteTodo
addTodo
and saveStorage
Delete Todo
/**
* Deletes the todo and saves into local storage
* @param {int} id the id of the todo
*/
const deleteTodo = (id) => {
todos.splice(id, 1);
saveStorage(todos);
};
Add Todo
/**
* Adds the todo to local storage
* @param {string} todo_text The text from the input box
*/
const addTodo = (todo_text) => {
if (todo_text === '') {
window.alert("Please enter some text")
} else {
let arr = [],
todo = {
value: todo_text,
completed: false,
};
if (todos === null) {
arr.push(todo);
} else {
arr = Object.values(todos);
arr.push(todo);
}
saveStorage(arr);
}
};
Save Storage
/**
* Communicates with the browsers localStorage
* @param {array} todos list of todos
*/
const saveStorage = (todos) => {
localStorage.setItem("todos", JSON.stringify(todos));
location.reload();
};
The start of the main section of code is the biggest so I’ll break it up into manageable chunks.
Start of the main code
Grab the input object by its ID. This is then passed to the variable input
. The second part grabs the to-dos out of local storage.
/**
* Start of main code
*/
let input = document.getElementById("todoInput");
/**
* Grab the todo list out of storage
*/
let todos =
localStorage.getItem("todos") !== null ?
JSON.parse(localStorage.getItem("todos")) :
null;
Display the Todos
The next section of the main code will only display the to-dos if there are any in Local Storage.
/**
* Display the todos
*/
if (todos !== null) {
for (let i = 0; i < todos.length; i++) {
const del_icon =
'<svg style="width:24px;height:24px" viewBox="0 0 24 24"><path fill="currentColor" d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z" /></svg>';
let list = document.getElementById("list"),
li = document.createElement("li"),
classes = ["todo-item", "level"];
li.classList.add(...classes);
li.id = i;
let comp = document.getElementsByClassName("item-value");
li.innerHTML =
'<div class="level-left"><div class="level-item item-value">' +
todos[i].value +
'</div></div><div class="level-right"><div class="level-item"><span class="icon del">' +
del_icon +
"</span></div></div>";
list.appendChild(li);
todos[i].completed === true ? comp[i].classList.toggle("completed") : comp[i].classList.toggle("completed", false);
}
}
Attach an Event to Todo Items
The last section of the main code attaches an event listener to every to-do item. As you can see in the last if statement. On pressing the delete icon, the item deletes from LocalStorage. On clicking a to-do item, a line appears through it to show that it’s completed.
/**
* Attaches an event listener to the individual todo items
*/
let todoElements = document.getElementsByClassName("todo-item");
for (let i = 0; i < todoElements.length; i++) {
todoElements[i].addEventListener("click", (e) => {
let id = todoElements[i].id,
level_right = todoElements[i].children[1].childNodes[0].firstChild,
level_left = todoElements[i].children[0];
if (e.target === level_right.lastChild) {
deleteTodo(id);
} else if (e.target === level_left.firstChild) {
todos[id].completed === true ? todos[id].completed = false : todos[id].completed = true
saveStorage(todos);
}
});
}
CSS
Finally the additional custom CSS
body{
background-color: rgb(235, 237, 243);
height: 100vh;
}
.list{
padding: 1.5rem;
}
.todo-item{
padding: 1rem;
background-color: #fff;
margin: 20px 0;
}
.todo-item .completed {
text-decoration: line-through;
}
Conclusion
As I stated this was a very simple JavaScript to-do list tutorial. Please feel free to fork the code and tweak it further. I will continue to add features to it. Please feel free to request other tutorials.
Discover more from WorldOWeb
Subscribe to get the latest posts sent to your email.