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.
Table of Contents
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
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 😀
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.
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);
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.
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