Tutorial - Create a To Do List with JavaScript

Tutorial - Create a To Do List with JavaScript
Check out my Github for my free-to-read JavaScript Ebook that covers all the new features from ES6 to 2019. If you want find a great place for interactive tutorials, i recommend Educative ( Disclaimer: the link it's affiliate ) where I'm currently finishing to build my JavaScript course.

Last updated on: 24/01/2020

In this short tutorial I am going to show you how you can create a simple todo list using only HTML, CSS and JavaScript. Before we start coding, we need to think about the features that we need and write them down on paper.  


User Stories for a to do list

Our todo list will be pretty simple, we want users to be able to:

  • add new items to the list by clicking an "add" button or by pressing "enter"
  • mark items as complete
  • completed items should disappear from the todo list and go inside a "completed" list
  • delete unwanted items from the list

Live demo of the todo list

To Do

    Completed



      Create the HTML for the to do list

      Now that we have a clear idea of what we want to do, let's start writing the markup for this project.

      Creating the structure of the todo list

      We need three components in our markup:

      • an header with an input tag for the user to enter an item
      • a section where to display tasks to do
      • a section where to display tasks completed
      <body>
        <div id="container">
          <header>
            <input type="text" placeholder="Enter an activity..." id="item" />
            <button id="add"></button>
          </header>
          <h2>To Do</h2>
          <ul class="todo" id="todo"></ul>
          <h2>Completed</h2>
          <ul class="completed" id="completed"></ul>
        </div>
      </body>

      We have a #container where we put our three components and inside of it we have an header and two ul. Next step is to add an icon to our add button

      <button id="add">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
          <path d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"></path>
        </svg>
      </button>

      The code that we added will display a "+" icon. For now inside our ul we put nothing, that's because we will use JavaScript to append elements to them. The structure we will use will be like this:

      <ul class="todo" id="todo">
        <li>item1
          <span class="buttons">
            <button class="remove"></button>
            <button class="complete"></button>
          </span>
        </li>
      </ul>

      Inside the li we have a span with two button, one to remove an element from the list and one to mark it as complete. Same as before, we will use svg icons.  

      Style the to do list with CSS

      This is how our todo list will look like:

      javascript-todolist-screenshot

      I apologize for the poor choice of colors but I am not a designer.  

      Define CSS variables

      We will make use of css variables to define our main colors like this:

          /* variables */
          :root {
            --font-color: #2d3436;
            --body-bg-color:#f7f1e3;
            --container-bg-color:#55efc4;
      
            --todo-bg-color: #00b894;
      
            --done-bg-color: #2ed573;
      
            --svg-normal-color: #2d3436;
            --svg-hover-color: #0984e3;
            --svg-complete-color: #0984e3;
            --svg-remove-color: #d63031;
            --svg-remove-hover-color: #ff4757;
          }
      educative banner

      Add google font to our project and style our container

      Next we move on to adding our new google font and styling the body and the container:

      * {
        font-family: 'Josefin Sans',
        sans-serif;
        padding: 0;
        margin: 0;
        color: var(--font-color);
      }
      
      body {
        background-color: var(--body-bg-color);
        display: flex;
        justify-content: center; 
      }
      
      #container {
        background-color: var(--container-bg-color);
        padding: 2%;
        min-height: 30vh;
        min-width: 30vw;
        width: auto;
        height: auto;
        margin-top: 10vh;
        border-radius: 5%;
      }

      Remember to add a link to google font inside of our head tag

      <link href="https://fonts.googleapis.com/css?family=Josefin+Sans:300,400,700" rel="stylesheet" />

      Style the h2

      Now let's style the h2:

      /* we make it inline-block so that our `h2::after` will only span the length of the `h2` and not the whole row */
      
      h2 {
        margin-bottom: 40px;
        margin-top: 40px;
        display: inline-block;
      }
      
      /* this one will add the underline that you can see in the screenshot */
      h2::after {
        content: "";
        border-bottom: 5px solid black;
        display: block;
        width: auto;
      }

      Style the header

      /* make the `header` a flex container to easily stack our elements in a column and automatically space them with `space-between` */
      header {
        display: flex;
        justify-content: space-between;
      }
      
      /* give some simple styling to our `input` tag */
      header input {
        min-height: 40px;
        width: 100%;
        border: none;
        padding-left: 5%;
        background-color: var(--body-bg-color);
      }
      
      header button {
        min-width: 40px;
        background: none;
        border: none;
        font-size: 2rem;
      }

      Style the list of todo items

      /* make the `li` stretch the whole width and add some border to it*/
      li {
        width: 100%;
        display: flex;
        align-items: center;
        background-color: var(--todo-bg-color);
        padding: 2%;
        box-sizing: border-box;
        border-radius: 10px;
        margin-bottom: 20px;
      }
      
      li .buttons {
        min-width: 100px;
        margin-left: auto;
        display: inline;
      }
      
      li .buttons button {
        background:none;
        min-width: 40px;
        border: none;
        font-size: 2rem;
      }
      
      /* apply a different style to the completed tasks */
      
      .completed li {
        background-color: var(--done-bg-color);
      }
      
      .completed .complete svg path {
        fill: var(--svg-complete-color);
      }

      Style the SVG icons

      Finally we can style our SVG icons like this:

      svg {
        max-width: 25px;
      }
      
      /* define the normal color */
      svg path {
        fill: var(--svg-normal-color);
      }
      
      /* change the color when we hover the icons*/
      button:hover svg path {
        fill: var(--svg-hover-color);
      }
      
      /* make the remove icon red*/
      .remove svg path {
        fill: var(--svg-remove-color);
      }
      
      /* make the remove icon a different color on hover */
      .remove:hover svg path {
        fill: var(--svg-remove-hover-color);
      }

      What we did was simply defining a base fill color for the icons and give the remove icon the color red by default and a lighter red on hover.

      Writing JavaScript for the todo list

      Now that we have a nice looking todo list it's time to make it work. First we will wrap everything in an EventListener to wait for our document to load before running the code:

          document.addEventListener("DOMContentLoaded", () => {
            // our code will go inside here
          });

      Define variables

      Next, we will start by defining some useful variables:

      // button to add new item
      let addButton = document.querySelector("#add");
      // input field to add new item
      let addInput = document.querySelector("#item");
      
      // add the svg icons for the buttons
      let removeSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M0 84V56c0-13.3 10.7-24 24-24h112l9.4-18.7c4-8.2 12.3-13.3 21.4-13.3h114.3c9.1 0 17.4 5.1 21.5 13.3L312 32h112c13.3 0 24 10.7 24 24v28c0 6.6-5.4 12-12 12H12C5.4 96 0 90.6 0 84zm416 56v324c0 26.5-21.5 48-48 48H80c-26.5 0-48-21.5-48-48V140c0-6.6 5.4-12 12-12h360c6.6 0 12 5.4 12 12zm-272 68c0-8.8-7.2-16-16-16s-16 7.2-16 16v224c0 8.8 7.2 16 16 16s16-7.2 16-16V208zm96 0c0-8.8-7.2-16-16-16s-16 7.2-16 16v224c0 8.8 7.2 16 16 16s16-7.2 16-16V208zm96 0c0-8.8-7.2-16-16-16s-16 7.2-16 16v224c0 8.8 7.2 16 16 16s16-7.2 16-16V208z"/></svg>` ;
      
      let completeSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"/></svg>` ;

      Add event listeners to our to do list

      We want our user to be able to add a new item by clicking on the add button or by pressing 'enter'. We will do so using two event listeners, one on the button itself for a click event and one that will listen to the keypress event and trigger when we press 'enter'.

      // user clicks on the add button
      // if there is text inside the item field then add it to the to do list
      
      addButton.addEventListener("click",function(){
        // grab the value of the input tag
        let  newItem = document.getElementById("item").value;
        // if the input tag is not empty then run our function to add an item
        if(newItem) { 
          // this function will add a new item to the todo list
          addItemTodo(newItem);
          // reset the input after we added a new item
          document.getElementById("item").value = "";
        }
      });
      
      // user press enter 
      
      addInput.addEventListener("keypress",function(e){
        // did the user press *enter*? if yes then continue
        if (13 === e.keyCode){
          // grab the value of the input tag
          let newItem = document.getElementById("item").value;
          // if the input tag is not empty then run our function to add an item
          if (newItem) {
            // this function will add a new item to the todo list
            addItemTodo(newItem);
            // reset the input after we added a new item
            document.getElementById("item").value = "";
          }
        }
      });

      We now have set up our two event listeners but we still can't add our items to the list because our function addItemTodo() has not been created yet. That is our next step.  

      Add items to the todo lit

      function addItemTodo(text){
        // grab the `ul`
        let list = document.getElementById("todo");
        // create an `li`
        let item = document.createElement('li');
        // set the inside of our `li` the same as the parameter that we passed in the function, which is going to be the value set by the user in the input field
        item.innerText = text;
      
        //create container for our buttons remove and complete
        let buttons = document.createElement('div');
        buttons.classList.add("buttons");
      
        // create the two buttons
      
        let remove = document.createElement('button');
        remove.classList.add('remove');
        // add the SVG icon to the button
        remove.innerHTML = removeSVG;
        // add event listener for remove
        // this function will be definedd later
        remove.addEventListener("click",removeItem);
      
        let complete = document.createElement('button');
        complete.classList.add('complete');
        // add the SVG icon to the button
        complete.innerHTML = completeSVG;
        // add event listener for complete
        // this function will be defined later
        complete.addEventListener("click",completeItem);
      
        // append the buttons to the div
        buttons.appendChild(remove);
        buttons.appendChild(complete);
      
        // append the whole div to the li
        item.appendChild(buttons);
      
        // prepend the `li` to the `ul`
        list.insertBefore(item,list.childNodes[0]);
      }

      Wow, that was a lot of code. Let's break it down and see what we did:

      • first we created a new li where we are going to add the text and the two buttons
      • next we created a div to hold our buttons
      • we created our two button, adding the SVG icon that we set previously
      • we added an event listener to our button to run functions that we define later
      • we appended our buttons to the div, appended the div to the li and prepended the li to the ul.

      We prepended the li because we want new tasks to appear on top and not at the bottom.  

      Complete and Remove tasks from the to do list

      Now it's time to create our last two functions: completeItem and removeItem:

      function completeItem(){
        // grab the `li` by targeting the parent of the parent of the button (button -> div -> li)
        let item = this.parentNode.parentNode;
        // grab the `ul` (li -> ul)
        let parent = item.parentNode;
        // grab the parent id
        let id = parent.id;
      
        // check if the item should go in the completed or if it should be re-added to todo by using a ternary operator
        let target = (id === "todo") ? document.getElementById("completed") : document.getElementById("todo");
        // remove the item to its current `ul`
        parent.removeChild(item);
        // add the item to the new `ul`
        target.insertBefore(item,target.childNodes[0]);
      } 

      Let's break down what we just did here:

      • first we grab our li and ul and the id of the ul.
      • if the id is "todo" it means that the item, once checked will have to go in the "completed" list and viceversa, if the id is "completed" it means that the user wants to re-add the item to the todo list
      • remove the item from its current ul
      • add the item to the new ul

      Similarly we can do this for removeItem:

      function removeItem(){
        // grab the `li` by targeting the parent of the parent of the button (button -> div -> li)
        let item = this.parentNode.parentNode;
        // grab the `ul` (li -> ul)
        let parent  = item.parentNode;
        // remove `li` from `ul`
        parent.removeChild(item);
      }

      Let's go over what this code does.

      • same as before we grab the li and ul
      • this time we don't need the id, because we just want to remove the item from the list, whatever list it is
      • we use removeChild(item) to remove the item.

      Conclusion

      You now have a fully working todo list where you can add, remove and mark tasks as complete. Why don't you try to add new functionalities to it? Or to style it in different ways?

      If you enjoyed this tutorial share it with your friends and if you create your own todo list, tag me on twitter so that I can see your result!


      Thank you very much for reading. Follow me on DevTo or on Twitter for more.

      complete guide to modern javascript alberto montalesi ebook bannerGet my ebook on Amazon and Leanpub
      Disclaimer: this website opts in affiliate programs for certain products


      NEWEST ARTICLES




      ABOUT ME

      author alberto montalesi profile picture

      Alberto is a software developer specialized in building enterpise software using Angular and author of the 'Complete guide to Modern JavaScript' ebook and course. In his free time he writes articles and tutorials on InspiredWebDev.com and Dev.to

      You can read more about him here