How to Build Modern ✅ To Do List JS App

In this tutorial I will share how I built To Do List JS app with CSS and Javascript.

I tried to make this project a complete To Do app with must have functions:

1️⃣ Add task (click [➕])

2️⃣ Edit task (double click on the task)

3️⃣ Delete task (click [⭕️])

4️⃣ Complete/Uncomplete task (click [O])

And I think I’ve succeeded 😊

As a UI designer, I decided to style To Do List Javascript Project in Dark UI style and also added some cool SVG animations ✅

So, let’s go and see the project in action below ⬇️

Project: To Do List JS App

// to do list js

See the Pen To Do List Javascript App (with Animations) by jsSecrets (@jssecrets) on CodePen.

to do list javascript project
to do list javascript project
Note
In this To Do list code I use popup window and sliding tabs. Both of these projects I explained in previous tutorials.
You can learn it here: popup window, sliding tabs. to do list js

Basic Working Principle

// to do list js

What is To Do List in a nutshell?

// to do list js

A to-do list is a list of tasks or activities that a person needs to accomplish or complete. It is a simple and effective way to keep track of what needs to be done and helps individuals stay organized and focused on their goals.

It typically includes a brief description of each task or activity. To-do lists can be used for personal or professional purposes and can help individuals manage their time more effectively and increase productivity.

Basic Functions

// to do list js

1️⃣ Add task

2️⃣ Complete/Uncomplete task

3️⃣ Edit task

4️⃣ Delete task

to do list js
to do list js

Working Principle

// to do list js

As mentioned above, there are four basic functions a To Do App must have.

1️⃣ Add task

// to do list js

User needs to add a task through a form.

2️⃣ Complete/Uncomplete task

// to do list js

There is have to be a button [✅] which user can click and task will move from “Active” to “Completed” list.

And another button [❎] to do a reverse action, if task has to be re-done.

3️⃣ Edit task

// to do list js

There is have to be a mechanism through which user can edit tasks.

4️⃣ Delete task

// to do list js

There is have to be a button [❌] which user can click and task will be deleted.

Simplest Working Version (SWV)

// to do list js

In my view the SWV look like this:

it has only two functions – “Add task” and “Complete/Delete task“.

1️⃣ Add task

// to do list javascript project

Basic form with input field and “Add Task” button on the page. User types a task, clicks a button and it being added to a list (ex: <ul>).

2️⃣ Complete/Delete task

// to do list javascript project

If task is completed or not relevant anymore, user clicks this task and its being removed from the list.

That is how I envision the simplest To Do List JS app 😀

Note
I use SVG animations for “Complete task” and “Delete task”. It is optional. You may just add simple icons or buttons.

Core concept

// to do list javascript project

Let’s start with core concept of how to do list javascript code works.

1️⃣ HTML

// to do list javascript code

HTML is simple.

There are three components in this project.

1️⃣ task list [<ul>] and “Add” [➕] button.

2️⃣ popup with “Add new task” form.

3️⃣ two tabs: Active and Completed tasks.

2️⃣ CSS

// to do list javascript code

Most of the CSS is straightforward in this simple todo list app.

The only exceptional parts are SVG animations on “Complete task” and “Delete task” events.

I mentioned, I use popup window and sliding tabs in this project. You can learn them in details here: popup, sliding tabs.

3️⃣ Javascript

// to do list javascript code

JS code for to do list app contains following functions:

1️⃣ showPopup() / closePopup(): To reveal and hide popup window when adding new task.

2️⃣ addNewTask(): To add new task.

3️⃣ deleteTask(): To delete task.

4️⃣ completeTask() / unCompleteTask(): To move task to “Completed” list and move back to “Active” list.

5️⃣ editTask(): To edit a task.

6️⃣ saveTask(): To save task that had just been edited.

7️⃣ setActiveTab(): To switch between “Active” and “Completed” tasks’ lists.

8️⃣ ifEmpty(): To check whether the “New task” input field is empty. To prevent adding empty tasks.

to do list javascript html css
to do list javascript html css

Code explanation

// to do list javascript html css

If you have understood the core concept and working principle of how to to do list javascript project is built, the code will be easy to grasp.

HTML

// to do list javascript html css

<body>

   <!-- ------------popup-------------- -->
   <!--learn it here: http://jssecrets.com/how-to-create-popup-javascript-modal-dialog/ -->

    <div class="popup">
      <div class="content">
        <h1 class="title-text">Add New Task</h1>
        <div class="input-container">
  <!-- input new list item (task) -->
          <input class="new-task-input" type="text" />
        </div>
        <div class="btn-container">
          <a class="btn add-new-task-button disabled">ADD</a>
        </div>
      </div>
      <span class="close-btn">
        <img src="img/cross.svg" alt="" />
      </span>
    </div>

    <div class="overlay"></div>
    <!-- ------------END popup-------------- -->

    <!-- tasks list -->
    <div class="tasks-list-container">
      <h1 class="tasks-list-title">Active tasks</h1>
      <ul class="tasks-list"></ul>

   <!-- add button to add item-->
      <div class="add-new-task-container">
        <button class="plus-button">
          <img src="img/plus.svg" />
        </button>
      </div>
  <!--END add button -->

    </div>
    <!--END tasks list -->


    <!-- tabs -->

 <!--learn it here: http://jssecrets.com/how-to-build-sliding-tabs-javascript/ -->

    <div class="tasks-list-settings">
      <ul class="tabs">
        <li class="tab active showActive">
          <span class="title">Active</span>
        </li>
        <li class="tab showCompleted">
          <span class="title">Completed</span>
        </li>
        <div class="slider"></div>
      </ul>
    </div>
    <!--END tabs -->

</body>

CSS

// to do list javascript html css

/*basic reset*/
*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Poppins", sans-serif;
}

ul {
  list-style: none;
}

/*button style*/
.btn {
  padding: 16px 32px;
  font-size: 1em;
  border: none;
  outline: none;
  cursor: pointer;
  text-align: center;
  border-radius: 9px;
  position: relative;
  transition: all 0.2s linear;
}

body {
  height: 100vh;
  background: hsl(225deg, 14%, 12%);
  position: relative;
  overflow: hidden;
  color: #292d34;
}


/*task list title*/
.tasks-list-title {
  text-align: center;
  margin-bottom: 40px;
  color: white;
  font-size: 32px;
}
@media (max-width: 320px) {
  .tasks-list-title {
    font-size: 24px;
  }
}

/*tasks list container*/
.tasks-list-container {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  opacity: 1;
  transition: all 0.7s ease-out;
}

/*tasks list*/
.tasks-list {
  width: 480px;
  margin-left: auto;
  margin-right: auto;
  border-radius: 10px;
  background-color: hsl(225deg, 14%, 17%);
  border: 1.5px solid rgba(0, 0, 0, 0.38);
  box-shadow: 0 8px 32px 0 hsl(225deg, 14%, 12%);
  backdrop-filter: blur(40px);
  padding: 24px;
}
@media (max-width: 575px) {
  .tasks-list {
    width: 400px;
    padding: 16px;
  }
}
@media (max-width: 440px) {
  .tasks-list {
    width: 360px;
    padding: 12px;
  }
}
@media (max-width: 400px) {
  .tasks-list {
    width: 300px;
  }
}
@media (max-width: 400px) {
  .tasks-list {
    width: 240px;
    padding: 8px;
  }
}

/*individual task*/
.tasks-list .task {
  width: 100%;
  padding: 16px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  transition: all 0.5s ease-out;
  background-color: hsl(225deg, 14%, 22%);
  backdrop-filter: blur(40px);
  border: 1px solid rgba(0, 0, 0, 0.33);
  box-shadow: 0 8px 32px 0 hsl(225deg, 14%, 12%);
}
@media (max-width: 320px) {
  .tasks-list .task {
    padding: 8px;
  }
}
.tasks-list .task:not(:first-of-type) {
  margin-top: 16px;
}
.tasks-list .task.completed {
  display: none;
}

/*complete task SVG button*/
.tasks-list .task .complete-task {
  display: block;
  width: 28px;
  height: 28px;
  min-width: 28px;
  border-radius: 50%;
  background-color: transparent;
  border: 2px solid #56d0f5;
  box-shadow: 0 0 12px rgba(86, 208, 245, 0.5);
  cursor: pointer;
}

/*border is used as an outline. I remove it to animate it*/
.tasks-list .task .complete-task.no-border {
  border: none;
}

/*.empty is used to perform indication of un-complition a task*/
/*border comes back*/
.tasks-list .task .complete-task.empty {
  border: 2px solid #56d0f5;
}
/*checkmark dissapears*/
.tasks-list .task .complete-task.empty .checkmark {
  opacity: 0;
  visibility: hidden;
}

/*checkmark*/
.tasks-list .task .complete-task .checkmark {
  vertical-align: middle;
  fill-rule: evenodd;
  clip-rule: evenodd;
  stroke-linejoin: round;
  stroke-miterlimit: 10;
  box-shadow: inset 0px 0px 0px #56d0f5;
  border-radius: 50%;
  background-color: transparent;
  pointer-events: none;
}
.tasks-list .task .complete-task .checkmark * {
  fill: transparent;
  stroke: transparent;
}
/*checkmark animation rules*/
.tasks-list .task .complete-task .checkmark.animated {
  animation: fill-checkmark 0.4s ease-in-out 0.4s forwards, scale 0.3s ease-in-out 0.9s both;
}

/*I animate the outline*/
.tasks-list .task .complete-task .checkmark.animated .circle {
  fill: none;
  stroke-width: 2;
  stroke-miterlimit: 10;
  stroke: #56d0f5;
  stroke-dasharray: 79;
  stroke-dashoffset: 79;
  animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}

/*and checkmark itself*/
.tasks-list .task .complete-task .checkmark.animated .check {
  fill: none;
  fill-rule: nonzero;
  stroke: #fff;
  stroke-width: 2.5px;
  stroke-dasharray: 23;
  stroke-dashoffset: 23;
  animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}

/* delete button - "cross" icon*/
.tasks-list .task .delete-task {
  display: block;
  width: 28px;
  height: 28px;
  min-width: 28px;
  border-radius: 50%;
  border: 2px solid hsl(354deg, 89%, 65%);
  box-shadow: 0 0 12px rgba(245, 86, 102, 0.5);
  background: transparent;
  cursor: pointer;
}

/*border is used as an outline. I remove it to animate it*/
.tasks-list .task .delete-task.no-border {
  border: none;
}

/*crossmark*/
.tasks-list .task .delete-task .crossmark {
  vertical-align: middle;
  fill-rule: evenodd;
  clip-rule: evenodd;
  stroke-linejoin: round;
  stroke-miterlimit: 10;
  box-shadow: inset 0px 0px 0px hsl(354deg, 89%, 65%);
  border-radius: 50%;
  background-color: transparent;
  pointer-events: none;
}
.tasks-list .task .delete-task .crossmark * {
  fill: transparent;
  stroke: transparent;
}

/*crossmark animation rules*/
.tasks-list .task .delete-task .crossmark.animated {
  animation: fill-crossmark 0.4s ease-in-out 0.4s forwards, scale 0.3s ease-in-out 0.9s both;
}

/*I animate an outline*/
.tasks-list .task .delete-task .crossmark.animated .circle {
  fill: none;
  stroke-width: 2;
  stroke-miterlimit: 10;
  stroke: hsl(354deg, 89%, 65%);
  stroke-dasharray: 79;
  stroke-dashoffset: 79;
  animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}

/*and cross stick #1 in delete button */
.tasks-list .task .delete-task .crossmark.animated .stick-1 {
  fill: none;
  stroke: #fff;
  stroke-width: 2px;
  stroke-dasharray: 18;
  stroke-dashoffset: 18;
  animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}

/*and cross stick #2 in delete button */
.tasks-list .task .delete-task .crossmark.animated .stick-2 {
  fill: none;
  stroke: #fff;
  stroke-width: 2px;
  stroke-dasharray: 18;
  stroke-dashoffset: 18;
  animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}

/*task name in the <ul>*/
.tasks-list .task .task-name {
  width: calc(100% - 84px);
  font-size: 22px;
  font-weight: 500;
  color: white;
  border: none;
  outline: none;
  user-select: none;
  background: transparent;
  cursor: pointer;
  padding: 12px;
  border-radius: 12px;
}
@media (max-width: 400px) {
  .tasks-list .task .task-name {
    font-size: 16px;
  }
}
.tasks-list .task .task-name::selection {
  background: #f5cd51;
  color: white;
}

/*visible outline when task is in editing process*/
.tasks-list .task .task-name.isEditing {
  border: 2px solid #f7ce51;
  box-shadow: 0 10px 25px rgba(245, 205, 81, 0.4);
  background: transparent;
}

/*to hide active tasks in "Completed" list*/
.tasks-list.showCompleted .task.active {
  display: none;
}

/*to display completed tasks in "Completed" list*/
.tasks-list.showCompleted .task.completed {
  display: flex;
}

/*checkmark*/
.tasks-list.showCompleted .task.completed .checkmark {
  box-shadow: inset 0px 0px 0px 30px #56d0f5;
  transform: none;
  pointer-events: none;
}
.tasks-list.showCompleted .task.completed .checkmark .circle {
  stroke-dashoffset: 0;
  fill: none;
  stroke-width: 2;
  stroke-miterlimit: 10;
  stroke: #56d0f5;
}
.tasks-list.showCompleted .task.completed .checkmark .check {
  stroke-dashoffset: 23;
  fill: none;
  fill-rule: nonzero;
  stroke: #fff;
  stroke-width: 2.5px;
}

/*new task container*/
.add-new-task-container {
  text-align: center;
  margin-top: 32px;
}

/* button to add item to the task list */
.add-new-task-container .plus-button {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  background-color: hsl(225deg, 14%, 17%);
  border: 2px solid rgba(0, 0, 0, 0.38);
  box-shadow: 0 8px 32px 0 hsl(225deg, 14%, 12%);
  backdrop-filter: blur(40px);
  transition: all 0.3s ease-out;
}
.add-new-task-container .plus-button:hover {
  transform: translateY(-2px);
}
.add-new-task-container .plus-button img {
  vertical-align: middle;
}

/*tabs: Active and Completed*/
.tasks-list-settings {
  width: 320px;
  border-radius: 10px;
  background-color: hsl(225deg, 14%, 17%);
  border: 1.5px solid rgba(0, 0, 0, 0.38);
  box-shadow: 0 8px 32px 0 hsl(225deg, 14%, 12%);
  backdrop-filter: blur(40px);
  padding: 16px;
  position: absolute;
  left: 50%;
  top: 5%;
  transform: translate(-50%, -5%);
  opacity: 1;
  transition: all 0.7s ease-out;
}
@media (max-width: 400px) {
  .tasks-list-settings {
    width: 240px;
    padding: 8px;
  }
}

/*learn sliding tabs here:
https://jssecrets.com/how-to-build-sliding-tabs-javascript/
*/

.tabs {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}
.tabs .slider {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 100%;
  width: 50%;
  background: linear-gradient(45deg, rgb(245, 205, 81) 0%, rgb(247, 206, 81) 100%);
  box-shadow: 0 10px 25px rgba(245, 205, 81, 0.4);
  border-radius: 9px;
  z-index: 0;
  transition: all 0.3s linear;
}
.tabs .tab {
  width: 50%;
  height: 100%;
  display: block;
  text-align: center;
  cursor: pointer;
  padding: 16px 0 16px 0;
  border-radius: 9px;
  font-size: 16px;
  z-index: 1;
  position: relative;
  color: #7c828d;
  transition: all 0.3s linear;
}
.tabs .tab:hover {
  color: #f7ce51;
}
.tabs .tab .title {
  font-size: 16px;
}
.tabs .tab.active .title {
  font-weight: 900;
  color: hsl(225deg, 14%, 12%);
}
.tabs .tab:nth-of-type(1).active ~ .slider {
  left: 0;
}
.tabs .tab:nth-of-type(2).active ~ .slider {
  left: 50%;
}

/*learn popup here:
How to Create Popup Javascript  🪟 [Modern]
*/ .overlay { position: fixed; z-index: 1000; width: 100%; height: 100%; top: 0; left: 0; background: hsla(225deg, 14%, 14%, 0.0025); transition: all 1s; opacity: 0; visibility: hidden; } .popup { position: fixed; top: 50%; left: 50%; width: 50%; height: auto; max-width: 630px; min-width: 320px; z-index: 2000; visibility: hidden; backface-visibility: hidden; transform: translateX(-50%) translateY(-50%); perspective: 1300px; } @media (max-width: 400px) { .popup { min-width: 240px; } } .popup .content { padding: 24px; padding-top: 48px; padding-bottom: 48px; border-radius: 9px; box-shadow: 0 10px 25px rgba(124, 130, 141, 0.2); transition: all 0.7s; transform: rotateX(-70deg); opacity: 0; transform-style: preserve-3d; background-color: hsl(225deg, 14%, 17%); backdrop-filter: blur(4.5px); -webkit-backdrop-filter: blur(4.5px); border: 1.5px solid rgba(0, 0, 0, 0.38); box-shadow: 0 8px 32px 0 hsl(225deg, 14%, 12%); } @media (max-width: 400px) { .popup .content { padding: 48px 12px; } } .popup .content .input-container { text-align: center; margin-bottom: 48px; } @media (max-width: 400px) { .popup .content .input-container { margin-bottom: 16px; } } .popup .content .input-container .new-task-input { width: 100%; border: 2px solid #f7ce51; box-shadow: 0 10px 25px rgba(245, 205, 81, 0.4); background: transparent; outline: none; font-size: 24px; padding: 16px; border-radius: 12px; color: white; transform: translateY(100px); transition: all 0.8s ease-out; opacity: 0.1; } @media (max-width: 400px) { .popup .content .input-container .new-task-input { font-size: 16px; padding: 8px; } } .popup .content .title-text { text-align: center; margin-bottom: 16px; transform: translateY(80px); transition: all 0.7s ease-out; opacity: 0.1; color: white; font-size: 32px; } @media (max-width: 400px) { .popup .content .title-text { font-size: 20px; } } .popup .content .btn-container { text-align: center; transform: translateY(120px); transition: all 0.9s ease-out; opacity: 0.1; } .popup .content .btn-container .btn { background: linear-gradient(45deg, rgb(245, 205, 81) 0%, rgb(247, 206, 81) 100%); box-shadow: 0 10px 25px rgba(245, 205, 81, 0.4); color: white; transition: all 0.9s ease; opacity: 0.9; } @media (max-width: 400px) { .popup .content .btn-container .btn { font-size: 16px; padding: 8px 12px; } } .popup .content .btn-container .btn:hover { opacity: 1; } .popup .content .btn-container .btn.disabled { background: #b9bec7; box-shadow: 0 8px 16px rgba(185, 190, 199, 0.0025); pointer-events: none; opacity: 0.5; } .popup .close-btn { position: absolute; top: 4px; right: 11px; font-size: 2em; color: white; cursor: pointer; transition: all 0.7s; opacity: 0; transform: rotateY(90deg); } .popup.show { visibility: visible; } .popup.show ~ .overlay { opacity: 1; visibility: visible; } .popup.show .content { transform: rotateX(0deg); opacity: 1; } .popup.show .content .title-text { transform: translateY(0px); opacity: 1; } .popup.show .content .new-task-input { transform: translateY(0px); opacity: 1; } .popup.show .content .btn-container { transform: translateY(0px); opacity: 1; } .popup.show .close-btn { opacity: 0.7; transform: rotateY(0deg); } .popup.show ~ .tasks-list-container { transform: translate(-50%, calc(-50% + 200px)); opacity: 0; } .popup.show ~ .tasks-list-settings { transform: translate(-50%, calc(-50% - 200px)); opacity: 0; } /*animations for checkmark and cross*/ /*stroke animation*/ @keyframes stroke { 100% { stroke-dashoffset: 0; } } /*scale effect animation*/ @keyframes scale { 0%, 100% { transform: none; } 50% { transform: scale3d(1.1, 1.1, 1); } } /*fill checkmark icon*/ @keyframes fill-checkmark { 100% { box-shadow: inset 0px 0px 0px 30px #56d0f5; } } /*fill cross icon*/ @keyframes fill-crossmark { 100% { box-shadow: inset 0px 0px 0px 30px hsl(354deg, 89%, 65%); } }

Javascript

// to do list javascript html css

// Varibles
//////////////////////////////////////////////
const plusButton = document.querySelector('.plus-button');
const popup = document.querySelector('.popup');
const addNewTaskButton = document.querySelector('.add-new-task-button');
const tasksList = document.querySelector('.tasks-list');
const newTaskInput = document.querySelector('.new-task-input');
const tabs = document.querySelectorAll('.tabs .tab');
const tasksListTitle = document.querySelector('.tasks-list-title');
const popupCloseBtn = document.querySelector('.close-btn');

//////////////////////////////////////////////
// Functions
//////////////////////////////////////////////
// Utility functions

// adding and removing classes will be used many times
// that is why I created utility functions
// DRY principle 😊

// addClass function - adds class to an element
const addClass = (element, className) => {
  element.classList.add(className);
};

// removeClass function - removes class from an element
const removeClass = (element, className) => {
  element.classList.remove(className);
};

// I've decided to create and use them,
// as there are many occurrences of this functionality
/////////////////////////////////ß/////////////

// 1. Show popup
// reveals popup window for adding new task
const showPopup = () => {
  addClass(popup, 'show');
};

// 2. Add new task
// adds new task
// I use prepared HTML
// each "Task" component consists of three elements
// "Complete" button. I use <svg>, as I have an animation effect.
// Input field. I use <input>, and not some text element, as I need to have editing functionality
// "Delete" button. I use <svg>, as I have an animation effect.
// Also pay attention that I added a "onclick" events right into the markup.
// It makes the life easier 😊
// Adding event listeners to dynamically created elements is not easy ))
const addNewTask = () => {
  tasksList.insertAdjacentHTML(
    'beforeend',
    `<li class="task active">

        <span class="complete-task" onclick="completeTask(event)">
        <svg
        class="checkmark"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 26 26"
      >
        <circle class="circle" cx="13" cy="13" r="12.5" />
        <path class="check" d="m5 13.807 4.773 4.84L21 7.353" />
      </svg>
        </span>

        <input class="task-name" ondblclick="editTask(event)" onclick="saveTask(event)" value="${newTaskInput.value}" type="text" readonly />

        <span class="delete-task" onclick="deleteTask(event)">
        <svg
        class="crossmark"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 26 26"
      >
        <circle class="circle" cx="13" cy="13" r="12.5" />
        <path class="stick-1" d="m6.69 6.69 12.62 12.62" />
        <path class="stick-2" d="M6.69 19.31 19.31 6.69" />
      </svg>
        </span>
      </li>`
  );

  // set states to default

  // clean "new task" input
  newTaskInput.value = '';
  // close popup
  removeClass(popup, 'show');
  // disable "Add" button
  addClass(addNewTaskButton, 'disabled');
};

// 3. Delete task animation
const deleteTask = (event) => {
  // animation classes
  addClass(event.currentTarget, 'no-border');
  addClass(event.currentTarget.firstElementChild, 'animated');

  // I use timer, so the animation could finish
  setTimeout(() => {
    // remove "Task" component from DOM
    event.target.parentElement.remove();
  }, 1800);
};

// 4. Complete task
const completeTask = (event) => {
  //
  // animation classes
  addClass(event.currentTarget, 'no-border');
  addClass(event.currentTarget.firstElementChild, 'animated');
  removeClass(event.currentTarget, 'empty');
  //
  // I set "unCompleteTask" function onClick
  // if user decides to move the task from "Completed" back into "Active"
  event.currentTarget.setAttribute('onclick', 'unCompleteTask(event)');

  // I use timer, so the animation could finish
  setTimeout(() => {
    addClass(event.target.parentElement, 'active');
    addClass(event.target.parentElement, 'completed');
    removeClass(event.target.firstElementChild, 'animated');
  }, 1800);
};

// 4.1. Uncomplete task
const unCompleteTask = (event) => {
  // animation classes
  removeClass(event.currentTarget, 'no-border');
  addClass(event.currentTarget, 'empty');
  //
  // I set "completeTask" function onClick
  // so user can move the task from "Active" to "Completed"
  event.currentTarget.setAttribute('onclick', 'completeTask(event)');

  // I use timer, so the animation could finish
  setTimeout(() => {
    addClass(event.target.parentElement, 'active');
    removeClass(event.target.parentElement, 'completed');
  }, 300);
};

// 5. Edit task
const editTask = (event) => {
  // I remove "readonly" attribute, so user can access the input
  event.currentTarget.removeAttribute('readonly');
  // highlighting input to indicate the active state
  addClass(event.currentTarget, 'isEditing');
};

// 6. Save task
const saveTask = (event) => {
  // I add "readonly" attribute, so user can't access the input
  event.currentTarget.setAttribute('readonly', 'readonly');
  // remove the highlighting
  removeClass(event.currentTarget, 'isEditing');
};

// 7. Set active tab
const setActiveTab = (event) => {
  // tabs indication
  removeClass(document.querySelector('.tab.active'), 'active');
  addClass(event.currentTarget, 'active');

  // toggle between "Active" and "Completed" tasks
  if (event.currentTarget.classList.contains('showCompleted')) {
    addClass(tasksList, 'showCompleted');
    tasksListTitle.innerText = 'Completed tasks';
  } else {
    removeClass(tasksList, 'showCompleted');
    tasksListTitle.innerText = 'Active tasks';
  }
};

// 8. Check if input is empty
const ifEmpty = () => {
  // if new "Task" is empty then the "Add" button is disabled
  if (!newTaskInput.value.trim().length || newTaskInput.value === '') {
    addClass(addNewTaskButton, 'disabled');
  } else {
    removeClass(addNewTaskButton, 'disabled');
  }
};

// 9. Close popup
const closePopup = () => {
  // clear the input
  newTaskInput.value = '';
  // close the popup
  removeClass(popup, 'show');
  // disable the button
  addClass(addNewTaskButton, 'disabled');
};

//////////////////////////////////////////////
// Event listeners
//////////////////////////////////////////////
// click event
plusButton.addEventListener('click', showPopup);
//
// click event
addNewTaskButton.addEventListener('click', addNewTask);
//
// click events for tabs
tabs.forEach((tab) => {
  tab.addEventListener('click', setActiveTab);
});
//
// input event
newTaskInput.addEventListener('input', ifEmpty);
//
// click event
popupCloseBtn.addEventListener('click', closePopup);
to do list js
to do list js

Advanced To Do List Project

// create todo list javascript

Advanced todo list apps can have a wide range of functions. Here are some features you can include in your app:

1️⃣ Task creation: Users can easily create new tasks or items to add to their todo list.

2️⃣ Prioritization: Users can set a priority level for each task or item on the todo list, allowing them to focus on the most important or urgent items first.

3️⃣ Reminders: Todo list apps can send notifications or reminders to users to ensure they complete their tasks on time.

4️⃣ Due dates: Users can set due dates for tasks or items, helping them stay on track and complete tasks by the desired deadline.

5️⃣ Categories or tags: Todo list apps can allow users to categorize or tag tasks or items for easier organization and tracking.

6️⃣ Notes or comments: Users can add notes or comments to tasks or items for additional details or context.

7️⃣ Syncing: Many to-do list apps offer syncing across multiple devices, allowing users to access and manage their to-do lists from their phone, tablet, or computer.

8️⃣ Sharing: Some to-do list apps allow users to share their lists with others, making them useful for collaboration or delegation of tasks.

9️⃣ Customization: To-do list apps can often be customized with different themes, backgrounds, or other design elements to suit users’ preferences.

Project Ideas

1️⃣ Code advanced to do list project. Add more functions. As explained above.

2️⃣ Code to do list using localStorage, so data can be saved.

Note
If you have some Javascript project in mind that you want me to explain, please write it in the comments.
to do list javascript code
to do list javascript code

What to do next?

// to do list js

You can check out my Javascript Calculator app project.

Resources

// to do list js

1️⃣ jssecrets.com

2️⃣ Work illustrations by Storyset

3️⃣ Work illustrations by Storyset

4️⃣ User illustrations by Storyset

5️⃣ Work illustrations by Storyset

6️⃣ How to Build Modern To Do List JS App Codepen

Ilyas Seisov

Ilyas Seisov

UI/Web designer and Javascript developer with Master's degree in Computer Science. He helps businesses transform ideas into reality with the power of design and code. Ilyas creates modern, aesthetic web applications and reads minds in a free time.

Leave a Reply

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