SKJ Blog

Menu

.
back to js articles

Build a Todo List App in Javascript

Shallom Kyle Jacinto

April 21st 2021 5 minute read

In this article, we will be building a todo list app in pure javascript, no framework, improve your coding skills by building a project.

Prerequisites

Atleast already know of javascript, array, array methods, doms, objects, events, template literals or you can find this on w3schools.com

Setup

Create a two files, the first one name it to todolist.html and then the second name it to main.js and save it.

todolist.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Todo List Tutorial</title>
  </head>
  <body>
    <h1>Todo List App</h1>
    
    <!-- form for creating todo -->
    <form id="todo-form">
      <input type="text" placeholder="text..." id="todoText">
      <button type="submit">Add todo</button>
    </form>
    
    <h2>List :</h2>
    
    <!-- form for our update todo -->
    <div id="updateForm" style="display: none">
      <input type="text"> 
      <button onclick="updateTodo()">update</button>
      <button onclick="cancel()">cancel</button>
    </div>
    
    <!-- our todos will be display here -->
    <ul id="todo-list">
      ...
    </ul>
    
    <script src="main.js"></script>
  </body>
</html>

We see that in our update form, we style it with display:none because this will be only block if our update button cliked, so we hide it to default.

And if we run it in our browser, we should see a form ...

Submit event

To handle the add todo event, in our main.js we add a varibale name form and we get the id of the add todo form.

const form = document.querySelector("#todo-form");

Then we add an addEventListener so we can listen what type of event in our todo-form. In our case, the type of event was listened is submit.

todolist.html

<form id="todo-form">
   ...
   <button type="submit"></button> <!-- `submit` event -->
</form>

main.js

form.addEventListener("submit", e => {
  e.preventDefault();
   
  ...
}

We pass a second parameter which is e, means event. We add a preventDefault() to prevent our form from being refresh.

For now try to add an alert("worked?") inside our todo form function and try to click the add todo button and you should see an alert.

After that inside our todo form function, we add a variable name textInput to define our todo text input.

todolist.html

<form id="todo-form">
   <input type="text" placeholder="text..." id="todoText">
   ...
</form>

main.js

let textInput = document.querySelector("#todoText");

And we set a new variable name text which we already get the value of textInput and trim it for validation.

let text = textInput.value.trim();

Validation

When submitting form, we dont like that our data has been submit with empty data, so we add an if else statement to validate our form.

if(!text) {
   alert('text is required!')
} else {
   ...
}

If user pass the validation, in our else statement, we add a another function name addTodo which will handle the add todo function for creating a new todo and we pass the text value as an argument to use it on addTodo function and we set the value of textInput to empty after creating a new todo.

form.addEventListener("submit", e => {
  ...
  } else {
    addTodo(text)
    textInput.value = ""
  }
})

function addTodo(todoText) {
  ...
}

Add Todo Function

In our addTodo function we named our parameter as a todoText that was pass from else statement.

Inside our addTodo function, we create an object variable name todo which will contain the todoText and the id of the value.

function addTodo(todoText) {
  const todo = {
    todoText,
    id: Date.now() // for id we use date for creating id's
  }
}

After that, we create a global variable todos which will contains all our object of an array value.

let todos = []

Inside our addTodo function, we push the todo object to the todos variable to save our data.

And we console our todos array to see the values.

function addTodo(todoText) {
  const todo = {
    ...
  }
  
  todos.push(todo)
  console.log(todos)
}

Run your todolist.html and try to add something, open your console and you should see an object of an array that contains your values.

Display Todo

Inside our addTodo function, we call the displayTodo function to display all our list.

function addTodo() {
   ...
   displayTodo(todos)
}

function displayTodo(todos) {
   ...
}

In our displayTodo function, we get the todo list for displaying our list and we create an element of li because we want to display it on unordered list.

let li = document.createElement("li");

and inside our li element, we use a template literals to display our text.

li.innerHTML = `
  <p id=${todo.id}> // we add an id so we can find what id element will be delete, update.
    <span>${todo.todoText}</span> // our text will display here 

    <button onclick="update(${todo.id})">update</button> // we pass the id so we can find what element to be updated
    <button onclick="deleteTodo(${todo.id})">delete</button> // we pass the id so we can find what element to be deleted
  </p>
  `
  })

And we use insertBefore to add an element as first element.

todoList.insertBefore(li, todoList.childNodes[0])

Run your web and try to add something, open your console and you'll see a that an li was added on ul tag which the text and id's are displayed.

Update Todo

Four updating our todo, we create a three function, the update() will only make the update form as block element to display it, we set the value of update form and we pass the id to find what element will be updated.

The updatedTodo() is for updating our todo in ui and updating our todo in array.

The cancel() is to cancel our update, reset the form and display it to none.

function update(todoId) {
  
  // We get the id of an element and nodes as the <span> is read as a 1 childnode .
  const list = document.getElementById(`${todoId}`).childNodes[1].innerHTML 
  
  // We set the update form value of the current value of list
  updateForm.childNodes[1].value = list
  
  //we set updateId to use it to updateTodo() func
  updateId = todoId
  
  // Display the form
  updateForm.style.display = "block"
}
function updateTodo() {
  // Get the element to be updated
  let list = document.getElementById(`${updateId}`).childNodes[1]
  
  // New text value from update form
  const newTodoText = updateForm.childNodes[1].value
  
  // Find index of the list by using an id and update it on array
  todos.findIndex(todo => {
    if(todo.id === updateId) {
      list.innerHTML = newTodoText //the element to be updated
      todo.todoText = newTodoText // the array to be updated
    }
  })
  
  // We console it to see if works
  console.log(todos)
  
  // Hide the form and reset
  updateForm.style.display = "none" 
  updateForm.childNodes[1].value = ""
}
function cancel() {
  // Hide the form and reset
  updateForm.style.display = "none" 
  updateForm.childNodes[1].value = ""
}

Delete Todo

For our deleteTodo(), we get the element id and its parent and we remove it on ui. We get the index of the array to so we can splice it according to its index and we console it to see if works.

function deleteTodo(todoId) {
  // delete the ui element
  const list = document.getElementById(`${todoId}`)
  list.parentElement.remove()
  
  // return the index of an array
  let listIndex = todos.findIndex(todo => {
    return todo.id === todoId
  })
  
  // delete the array according to its index
  todos.splice(listIndex, 1)
  
  console.log(todos)
}

Testing app

I add a tree list and console it.

And we see in our console that there is 3 array which is our list with its index, the 0,1,2.

We update the code element.

And also in our array has been updated.

We delete the sleep element.

And also our array, the list is also been deleted.

Great! Now you have full working todo list app.

Source code

For source code, open this github repo


js project app