Category: JavaScript

Simple To-Do App with JavaScript Only

Although the jQuery library has been a fantastic tool used by almost every front-end developer, it’s still worth taking sometime and understanding how JavaScript works. Using newly established JavaScript functions, it’s almost as simple to create basic applications with JavaScript as it is with jQuery.

Additionally, understanding fundamental JavaScript techniques will go a long way in helping you understand how to build even more complicated jQuery applications.

The To-Do App Project

In short, this project consists of new JavaScript functions that will help interact with the dom. Here’s what the completed application looks like.

See the Pen Simple Todo App with JavaScript Only by Richard (@barkins) on CodePen.

Before we began coding any JavaScript, lets start by putting together the HTML structure. I’m going to build everything into a SECTION tag which will contain an INPUT for a new task, a list of incomplete tasks and a list of complete tasks with a delete BUTTON.

HTML

<section class="container">
  
  <h1>To-Do App</h1>
  
  <div class="new-task-container box">
  
  <label for="new-task">Add New Task</label>
  <input type="text" id="new-task">
    <button id="addTask">Add Task</button>
  
  </div>
  
  <div class="todo-list box">
  
    <h2>Incomplete Tasks</h2>
    <ul>
      <li><input type="checkbox"><label>Task Name</lable></li>
      <li><input type="checkbox"><label>Task Name</lable></li>
      <li><input type="checkbox"><label>Task Name</lable></li>
    </ul>
  
  </div><!--/todo-list-->
  
  <div class="complete-list box">
    
    <h2>Completed Tasks</h2>
    <ul>
      <li>Task Name <button class="delete">Delete</button></li>

    </ul>   
    
  </div><!--/complete-list-->
</section><!--/container-->

CSS

There are a number of ways to style this, but for my demo I’m showcasing as basic of a design as possible, just for presentation purposes.

* {
  box-sizing: border-box;
}

.container {
  background:#d3efdf;
  padding:25px;
  max-width:760px;
  margin:25px auto;
  overflow:hidden;
  border-radius:10px;
  border:4px solid #b1c9bb;
  font-family: sans-serif;
}

h1, h2 {
  margin:0;
  text-align:center;
  text-transform:uppercase;
 
}

h2 {
  font-size:20px;
  text-align:center;
  border-bottom:1px solid #b1c9bb;
  padding: 0 0 10px;
  color:#57635c;
    
}

.new-task-container {
  text-align:center;
}

.box {
  padding:10px 15px;
  border:2px solid #b1c9bb;
  border-radius:5px;
  background:#FFF;
  margin:15px 0;
}


.todo-list {
  float:left;
  width:46%;
}

.complete-list {
  float:right;
  width:46%;
}

ul {
  list-style:none;
  padding:0;
  margin:0;
}

li{
  padding:10px;
  border-bottom:1px dotted #CCC;
}

.delete {
  float:right;
}

JavaScript

Here’s the fun part, good old (new) JavaScript. Start off by assigning variables you plan on using throughout the project.

  • newTask – Store the input with the id of “new-task” which we will use to grab whatever was typed into the field.
  • addTaskBtn – Store the button used to initiate adding the task to the to-do list.
  • toDoUl – This variable will store the unordered to-do list of LIs.
  • completeUl – You guessed, this variable will store the unordered list of completed tasks.
//SELECT ELEMENTS AND ASSIGN THEM TO VARS
var newTask = document.querySelector('#new-task');
var addTaskBtn = document.querySelector('#addTask');

var toDoUl = document.querySelector(".todo-list ul");
var completeUl =  document.querySelector(".complete-list ul");

Next up, we’ll want to create our first functions. This function will create the new task. It will generate the LI HTML element, the input field and the label.

Once it’s complete constructing the list item with the newly entered task by the user, it will return the result back to the function. In other words, once this function finishes running, you can call it to grab its result. We’ll cover that soon.

//CREATING THE ACTUAL TASK LIST ITEM
var createNewTask = function(task){
  console.log("Creating task...");
  
  //SET UP THE NEW LIST ITEM
  var listItem = document.createElement("li"); //<li>
  var checkBox = document.createElement("input"); //checkbox
  var label = document.createElement("label"); // <label>
  
  
  //PULL THE INPUTED TEXT INTO LABEL
  label.innerText = task;
  
  //ADD PROPERTIES
  checkBox.type = "checkbox";
  
  //ADD ITEMS TO THE LI
  listItem.appendChild(checkBox);
  listItem.appendChild(label);
  //EVERYTHING PUT TOGETHER
  return listItem;    
};

Now that we’ve constructed our new task item, all with the LI, the INPUT and CHECKBOX, we’re ready to actually add it to the to-do list.

As you’ll notice through out this application, I use a lot of “console.log()” functions to output text into the console so that I can see if things are working as they should. It’s just a good problem solving technique.

This function runs the previous function we created which then returns the result and stores it into the listItem variable. It then appends (adds) to the listItem variable to the children of the toDoUl variable we created at the start.

Here’s what that function looks like.

//ADD THE NEW TASK INTO ACTUAL INCOMPLETE LIST
var addTask = function(){
  console.log("Adding task...");
  //FOR CLARITY, GRAB THE INPUTTED TEXT AND STORE IT IN A VAR
  var listItem = createNewTask(newTask.value);
  //ADD THE NEW LIST ITEM TO LIST
  toDoUl.appendChild(listItem); 
  //CLEAR THE INPUT
  newTask.value="";
  
  //BIND THE NEW LIST ITEM TO THE INCOMPLETE LIST
  bindIncompleteItems(listItem, completeTask);
};

Now that we have the function created that pulls the newly entered task into the to-do list. We’ll need to create a function that allows the user to hit the checkbox and have the item move to the completed list.

When the user hits the checkbox, the completeTask function will launch (we’ll cover further down). Using the this we can reference the element’s parentNode which happens to be the LI it’s stored in.

Since the completed list has a delete button, we’ll need to create it here and also remove the checkbox from the list item.

Once the delete button has been created and the checkbox removed, we can have the function add the newly completed list item to the completed tasks list.

Lastly, since the the completed task list has now changed, we’ll need to rebind the elements in that list so that we can reference them later.

var completeTask = function(){
  
  //GRAB THE CHECKBOX'S PARENT ELEMENT, THE LI IT'S IN
  var listItem = this.parentNode;
  
  //CREATE AND INSERT THE DELETE BUTTON
  var deleteBtn = document.createElement("button"); // <button>
  deleteBtn.innerText ="Delete"; 
  deleteBtn.className = "delete";
  listItem.appendChild(deleteBtn);
  
  //SELECT THE CHECKBOX FROM THE COMPLETED CHECKBOX AND REMOVE IT
  var checkBox = listItem.querySelector("input[type=checkbox]");
  checkBox.remove();
  
  //PLACE IT INSIDE THE COMPLETED LIST
  completeUl.appendChild(listItem); 
  
  //BIND THE NEW COMPLETED LIST
  bindCompleteItems(listItem, deleteTask);
  
};

The deleteTask function is the simplest function here. It grabs the parent of the delete button and removes it using the JavaScript function, removeChild.

//DELETE TASK FUNCTIONS
var deleteTask = function(){
  console.log("Deleting task...");
  
  var listItem = this.parentNode;
  var ul = listItem.parentNode;
  
  ul.removeChild(listItem);  
};

Whenever we interact with both lists, we’ll have to run a function that will remind all the list items so that we can continue interacting with them.

In other words, whenever webpage first loads, the functions will see all the HTML elements. However, if we start adding and removing elements, the webpage no longer knows that those elements have been add or removed. Therefore we need the following set of functions to continuously rebind changes on the page.

//A FUNCTION THAT BINDS EACH OF THE ELEMENTS THE INCOMPLETE LIST

var bindIncompleteItems = function(taskItem, checkBoxClick){  
  console.log("Binding the incomplete list...");
  
  //BIND THE CHECKBOX TO A VAR
  var checkBox = taskItem.querySelector("input[type=checkbox]");
  
  //SETUP EVENT LISTENER FOR THE CHECKBOX
  checkBox.onchange = checkBoxClick;  
}; 


//A FUNCTIONM THAT BINDS EACH OF THE ELEMTS IN THE COMPLETE LIST
var bindCompleteItems = function(taskItem, deleteButtonPress){
  console.log("Binding the complete list...");
  
  //BIND THE DELETE BUTTON
  var deleteButton = taskItem.querySelector(".delete");
   
  //WHEN THE DELETE BUTTIN IS PRESSED, RUN THE deleteTask function
  deleteButton.onclick = deleteButtonPress;
    
};


for(var i=0; i < toDoUl.children.length; i++) {
  bindIncompleteItems(toDoUl.children[i], completeTask);
}

for(var i=0; i < completeUl.children.length; i++) {
  bindCompleteItems(completeUl.children[i], deleteTask);
}

And finally the last function of the project, the one that starts it all. This event listeners waits until a user hit the add task button and launches the addTask function.

addTaskBtn.addEventListener("click", addTask);
Filed under: CSS3, HTML5, JavaScript