Yet Another Simple JavaScript To-Do List

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 always a beginners project in any programming language. If I chuck another one in the mix it may be useful, it may be not. 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.

Prerequisites

A modern browser and a text editor. I recommend VS Code. 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 JavaScript To-do list tutorial.

Download

Download WOW To-do 14 downloads

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:

  1. Page loads and displays items, if any.
  2. Enter text in the input.
  3. Hit enter or the save button.
  4. Item saves to local storage.
  5. The page refreshes

There is the main section of 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 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>

JavaScript To-do List Code

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

/**
 * 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);
};
/**
 * 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);
  }

};
/**
 * 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. 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;

The next section of the main code displays the to-dos if there is any.

/**
 * 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);
  }
}

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);
    }
  });
}

Finally the 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

JavaScript to-do list
Final display with 1 item marked as complete

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

Topics:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.