JavaScript Course | Task Tracker Project

13 Jun, 2022
Previous article: JavaScript Course | Objects in JavaScriptProject Introduction In this final article of this course, we will learn how to make a simple javascript application where we can add tasks, delete tasks and edit them too. Pure(Vanilla) JavaScript has been used and we will also make use of DOM manipulation a lot so one of the main prerequisites is HTML | DOM. 
Project Structure 
index.html styles.css list.js
index.html 
HTML
<!DOCTYPE html><html><head>  <title>Task Tracker</title>  <link rel="stylesheet" href="styles.css" type="text/css"    media="screen" charset="utf-8"></head> <body>  <div class="container">      <p>      <label for="new-task" class="middle">Add Task</label>    <input id="new-task" type="text"><button>Add Task</button>    </p>       <h3 class="middle">Todo</h3>    <ul id="incomplete-tasks">    </ul>     <h3 class="middle">Completed Tasks</h3>    <ul id="completed-tasks">    </ul>  </div>   <script type="text/javascript" src="list.js"></script> </body> </html>
Explanation: The above HTML code contains simple list tags and one text field which we will populate with text when we add, delete tasks. Certain classes are assigned which we make use of while getting that particular element by DOM or by styling it inside the styles.css file. All the above content is inside a div with class ‘container’ that has its own styling and attributes.  
styles.css 
CSS
/* Basic Style */body {  background: #ecf0f1;  color: #333;  font-family: Lato, sans-serif;} .container {  display: block;  width: 500px;  margin: 50px auto 0;} ul {  margin: 0;  padding: 0;} li * {  float: left;} li,h3 {  clear: both;  list-style: none;} input,button {  outline: none;} button {  background: none;  border: 0px;  color: #888;  font-size: 15px;  width: 100px;  margin: 10px 0 0;  font-family: Lato, sans-serif;  cursor: pointer;} button:hover {  color: #333;} /* Heading */h3,label[for='new-task'] {  color: #333;  font-weight: 700;  font-size: 15px;  border-bottom: 2px solid #333;  padding: 30px 0 10px;  margin: 0;  text-transform: uppercase;} input[type="text"] {  margin: 0;  font-size: 18px;  line-height: 18px;  height: 18px;  padding: 10px;  border: 1px solid #ddd;  background: #fff;  border-radius: 6px;  font-family: Lato, sans-serif;  color: #888;} input[type="text"]:focus {  color: #333;} .middle {  text-align: center;}  /* New Task */label[for='new-task'] {  display: block;  margin: 0 0 20px;} input#new-task {  float: right;  width: 318px;} p>button:hover {  color: #0FC57C;} /* Task list */li {  overflow: hidden;  padding: 20px 0;  border-bottom: 1px solid #eee;} li>input[type="checkbox"] {  margin: 0 10px;  position: relative;  top: 15px;} li>label {  font-size: 18px;  line-height: 40px;  width: 237px;  padding: 0 0 0 11px;} li>input[type="text"] {  width: 226px;} li>.delete:hover {  color: #CF2323;} /* Completed */#completed-tasks label {  text-decoration: line-through;  color: #888;} /* Edit Task */ul li input[type=text] {  display: none;} ul li.editMode input[type=text] {  display: block;} ul li.editMode label {  display: none;}
Note: The above HTML and CSS files are for the presentation part mainly. 
list.js 
Javascript
// Add a new task.let taskInput = document.getElementById("new-task"); // first buttonlet addButton = document.getElementsByTagName("button")[0]; // ul of #incomplete-taskslet incompleteTaskHolder = document.getElementById("incomplete-tasks"); // completed-taskslet completedTasksHolder = document.getElementById("completed-tasks");  /*---- Part 1 ----*/// function to create new task itemlet createNewTaskElement = function (taskString) {      let listItem = document.createElement("li");      // input (checkbox)    let checkBox = document.createElement("input"); // checkbox    // label    let label = document.createElement("label"); // label    // input (text)    let editInput = document.createElement("input"); // text    // button.edit    let editButton = document.createElement("button"); // edit button      // button.delete    let deleteButton = document.createElement("button"); // delete button      label.innerText = taskString;      // Each elements, needs appending    checkBox.type = "checkbox";    editInput.type = "text";       // innerText encodes special characters, HTML does not.    editButton.innerText = "Edit";        editButton.className = "edit";    deleteButton.innerText = "Delete";    deleteButton.className = "delete";      // and appending.    listItem.appendChild(checkBox);    listItem.appendChild(label);    listItem.appendChild(editInput);    listItem.appendChild(editButton);    listItem.appendChild(deleteButton);    return listItem;}/*---- Part 2 ----*/let addTask = function () {    console.log("Add Task...");      let listItem = createNewTaskElement(taskInput.value);      if (taskInput.value == "") {        return;    }      // Append listItem to incompleteTaskHolder    incompleteTaskHolder.appendChild(listItem);    bindTaskEvents(listItem, taskCompleted);      taskInput.value = "";  }  /*---- Part 3 ----*/let editTask = function () {    console.log("Edit Task...");    console.log("Change 'edit' to 'save'");        let listItem = this.parentNode;      let editInput = listItem.querySelector('input[type=text]');    let label = listItem.querySelector("label");    let containsClass = listItem.classList.contains("editMode");    // If class of the parent is .editmode    if (containsClass) {        label.innerText = editInput.value;    } else {        editInput.value = label.innerText;    }    listItem.classList.toggle("editMode");}  /*---- Part 4 ----*/let deleteTask = function () {    console.log("Delete Task...");      let listItem = this.parentNode;    let ul = listItem.parentNode;    // Remove the parent list item from the ul.    ul.removeChild(listItem);  }  /*---- Part 5 ----*/  let taskCompleted = function () {    console.log("Complete Task...");      // Append the task list item to the #completed-tasks    let listItem = this.parentNode;    completedTasksHolder.appendChild(listItem);    bindTaskEvents(listItem, taskIncomplete);  }  /*---- Part 6 ----*/let taskIncomplete = function () {    console.log("Incomplete Task...");    // Mark task as incomplete.    let listItem = this.parentNode;    incompleteTaskHolder.appendChild(listItem);    bindTaskEvents(listItem, taskCompleted);}  /*---- Part 7 ----*/addButton.onclick = addTask;addButton.addEventListener("click", addTask);  let bindTaskEvents = function (taskListItem, checkBoxEventHandler) {    console.log("bind list item events");    // select ListItems children    let checkBox = taskListItem.querySelector("input[type=checkbox]");    let editButton = taskListItem.querySelector("button.edit");    let deleteButton = taskListItem.querySelector("button.delete");        // Bind editTask to edit button.    editButton.onclick = editTask;    // Bind deleteTask to delete button.    deleteButton.onclick = deleteTask;    // Bind taskCompleted to checkBoxEventHandler.    checkBox.onchange = checkBoxEventHandler;}  /*---- Part 8 ----*/// cycle over incompleteTaskHolder ul list items// for each list itemfor (let i = 0; i < incompleteTaskHolder.children.length; i++) {      // bind events to list items children(tasksCompleted)    bindTaskEvents(incompleteTaskHolder.children[i], taskCompleted);}  // cycle over completedTasksHolder ul list itemsfor (let i = 0; i < completedTasksHolder.children.length; i++) {    // bind events to list items children(tasksIncompleted)    bindTaskEvents(completedTasksHolder.children[i], taskIncomplete);}
Explanation 
Part 1 The way this function works is that it takes the ‘inputString’ i.e the text that we pass inside the HTML text field as a task and then it creates several elements using DOM properties and append them specific classes. After appending we insert all the elements inside the list as listItems.Part 2 This addTask function is called when we click the button ‘addButton'(line 115) and then inside it we create a listItem with the value the user entered and then check the value, as it must not be an empty string then we simply add the above value inside the ‘inputTaskHolder’ and finally setting the value inside it as an empty string before calling the ‘bindFunction’.  Part 3 This code function is used to edit an existing task and we do so keeping track of the parent node and then a simple if-else check that whether the ‘editMode’ button is clicked or not, if clicked then simply assign the value of the label innerText to value inside the editInput, if not then vice versa. Then after editing, we toggle the value of the ‘editMode’ as we have edited.  Part 4 In this part we delete a task and the way we do it by making use of the parent node of the current node and then storing the parent of the parent node and then simply deleting the child of this node.  Part 5 In this function we mark the task as complete by simply appending the child of the parent node inside the completeTaskHolder element and then calling the bindFunction.  Part 6 In this function we mark the task as incomplete by simply appending the child of the parent node inside the inCompleteTaskHolder element and then calling the bindFunction.  Part 7 In this part we call the BindFunction where we respond to the several user interaction activities and make several buttons work.  Part 8 In this final section we iterate over several parts binding children with the help of for loop inside the incomplete and complete TaskHolder element. 
Part 1 The way this function works is that it takes the ‘inputString’ i.e the text that we pass inside the HTML text field as a task and then it creates several elements using DOM properties and append them specific classes. After appending we insert all the elements inside the list as listItems.
The way this function works is that it takes the ‘inputString’ i.e the text that we pass inside the HTML text field as a task and then it creates several elements using DOM properties and append them specific classes. After appending we insert all the elements inside the list as listItems.
Part 2 This addTask function is called when we click the button ‘addButton'(line 115) and then inside it we create a listItem with the value the user entered and then check the value, as it must not be an empty string then we simply add the above value inside the ‘inputTaskHolder’ and finally setting the value inside it as an empty string before calling the ‘bindFunction’.  
This addTask function is called when we click the button ‘addButton'(line 115) and then inside it we create a listItem with the value the user entered and then check the value, as it must not be an empty string then we simply add the above value inside the ‘inputTaskHolder’ and finally setting the value inside it as an empty string before calling the ‘bindFunction’.  
Part 3 This code function is used to edit an existing task and we do so keeping track of the parent node and then a simple if-else check that whether the ‘editMode’ button is clicked or not, if clicked then simply assign the value of the label innerText to value inside the editInput, if not then vice versa. Then after editing, we toggle the value of the ‘editMode’ as we have edited.  
This code function is used to edit an existing task and we do so keeping track of the parent node and then a simple if-else check that whether the ‘editMode’ button is clicked or not, if clicked then simply assign the value of the label innerText to value inside the editInput, if not then vice versa. Then after editing, we toggle the value of the ‘editMode’ as we have edited.  
Part 4 In this part we delete a task and the way we do it by making use of the parent node of the current node and then storing the parent of the parent node and then simply deleting the child of this node.  
In this part we delete a task and the way we do it by making use of the parent node of the current node and then storing the parent of the parent node and then simply deleting the child of this node.  
Part 5 In this function we mark the task as complete by simply appending the child of the parent node inside the completeTaskHolder element and then calling the bindFunction.  
In this function we mark the task as complete by simply appending the child of the parent node inside the completeTaskHolder element and then calling the bindFunction.  
Part 6 In this function we mark the task as incomplete by simply appending the child of the parent node inside the inCompleteTaskHolder element and then calling the bindFunction.  
In this function we mark the task as incomplete by simply appending the child of the parent node inside the inCompleteTaskHolder element and then calling the bindFunction.  
Part 7 In this part we call the BindFunction where we respond to the several user interaction activities and make several buttons work.  
In this part we call the BindFunction where we respond to the several user interaction activities and make several buttons work.  
Part 8 In this final section we iterate over several parts binding children with the help of for loop inside the incomplete and complete TaskHolder element. 
In this final section we iterate over several parts binding children with the help of for loop inside the incomplete and complete TaskHolder element. 
Output:(Before adding any task) 
Output:(After adding task) 
Output:(After Completing the task) 
Next article: JavaScript Course | Practice Quiz-3 
sagar0719kumar
gabaa406
vinayedula
JavaScript-Course
JavaScript
Web Technologies
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Difference between var, let and const keywords in JavaScript
Differences between Functional Components and Class Components in React
Remove elements from a JavaScript Array
How to append HTML code to a div using JavaScript ?
Difference Between PUT and PATCH Request
Top 10 Projects For Beginners To Practice HTML and CSS Skills
Installation of Node.js on Linux
Difference between var, let and const keywords in JavaScript
How to insert spaces/tabs in text using HTML/CSS?
How to fetch data from an API in ReactJS ?