Front-end Machine Coding Series #1 : TO-DO App in Vanilla JS


In many front-end machine coding rounds or take-home assignments, candidates are asked to build a simplified version of a real-world application under time constraints. One such classic challenge is the TO-DO APP.
At first glance, this may seem trivial, but don't be fooled. Building a TO-DO app isn't just about adding and removing tasks. It's about demonstrating your ability to:
Structure clean and modular code in Vanilla JavaScript (no frameworks)
Design a user-friendly interface
Handle user input and errors gracefully
Maintain code quality under pressure
In this post, we’ll walk through the problem statement, requirements, and how you can approach building a fully-functional TO-DO application from scratch. Whether you're preparing for a machine coding interview or just brushing up your fundamentals, this guide will help you sharpen your front-end skills.
Problem Statement
Build a TO-DO Application using Vanilla JavaScript.
You should be able to:
Add new tasks
Edit existing tasks
Delete tasks
The interface must be clean and easy to use, and the code base should reflect best practices in readability and maintainability.
HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TODO APP</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<h2>Todo List</h2>
<form class="todo-form">
<input placeholder="Enter Todo" class="todo-input" type="text" />
<button class="todo-submit">Add Todo</button>
</form>
<ul class="todo-list"></ul>
<script src="./todo.js"></script>
</body>
</html>
CSS Code:
/* General Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: #f5f7fa;
display: flex;
flex-direction: column;
align-items: center;
padding: 40px 20px;
min-height: 100vh;
}
/* Heading */
h2 {
font-size: 2.2rem;
margin-bottom: 20px;
color: #333;
}
/* Form Styling */
.todo-form {
display: flex;
gap: 10px;
margin-bottom: 25px;
width: 100%;
max-width: 500px;
}
.todo-input {
flex: 1;
padding: 12px 16px;
border: 1px solid #ccc;
border-radius: 8px;
font-size: 1rem;
outline: none;
transition: border 0.3s ease;
}
.todo-input:focus {
border-color: #4f46e5;
}
.todo-submit {
padding: 12px 18px;
background-color: #4f46e5;
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
cursor: pointer;
transition: background 0.3s ease;
}
.todo-submit:hover {
background-color: #4338ca;
}
/* Todo List Styling */
.todo-list {
list-style: none;
width: 100%;
max-width: 500px;
}
.todo-list li {
background: #ffffff;
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.todo-list li.completed {
text-decoration: line-through;
opacity: 0.6;
}
.todo-list li button {
background: #ffffff;
color: white;
border: none;
border-radius: 6px;
padding: 6px 10px;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.3s ease;
}
.todo-list li button:hover {
background: #a39f9f;
}
JavaScript Code:
document.addEventListener("DOMContentLoaded", () => {
const todoSubmit = document.querySelector(".todo-submit");
const todoInput = document.querySelector(".todo-input");
const todoList = document.querySelector(".todo-list");
let editTodoMode = false;
let currentTodoItemToEdit = null;
document.addEventListener("submit", (e) => {
e.preventDefault();
const todoText = todoInput.value.trim();
if (todoText !== "") {
//we'll check if we're editing a todo or adding
if (editTodoMode) {
//assign the updated value to current selected todo item.
currentTodoItemToEdit.firstChild.textContent = todoText;
//change the submit button text
todoSubmit.innerText = "Add Todo";
editTodoMode = false;
currentTodoItemToEdit = null;
todoInput.value = "";
} else {
addTodo(todoText);
todoInput.value = "";
}
} else {
//console.log("Please enter a valid value in TODO");
alert("Please enter a valid text in TODO");
}
});
/* We can add a event handler for each todo item but that we'll decrease the performance. So we'll apply the concept
of event delegation to make use of target */
todoList.addEventListener("click", (e) => {
//capture the target element
const target = e.target;
//check if it's a button
if (target.tagName == "BUTTON") {
//if its a button, then capture the parent node(todoItem)
const todoItem = target.parentNode;
//check for edit or delete
if (target.innerText == "❌") {
todoItem.remove();
} else if (target.innerText == "✏️") {
editTodoMode = true;
//assign the clicked todo item to currentTodoItemToEdit
currentTodoItemToEdit = todoItem;
//change the button text
todoSubmit.innerText = "Edit Todo";
//assign the selected todo value to input, when adding todo we take li as first child in todoItem
todoInput.value = todoItem.firstChild.textContent;
todoInput.focus();
}
}
});
const addTodo = (todoText) => {
const todoItem = document.createElement("li");
const editButton = document.createElement("button");
const removeButton = document.createElement("button");
todoItem.innerHTML = `<span>${todoText}</span>`;
editButton.innerText = `✏️`;
removeButton.innerText = `❌`;
todoItem.appendChild(editButton);
todoItem.appendChild(removeButton);
todoList.appendChild(todoItem);
};
});
Wrap-Up
So now, you have gained hands-on experience in:
Structuring a real-world app using just HTML, CSS, and JavaScript
Implementing CRUD operations from scratch
Writing clean, maintainable code without relying on libraries or frameworks
Applying basic CSS styling to make the app intuitive
Happy coding! If you have any questions or suggestions you'd like to explore further, feel free to drop a comment below.
See you in the next blog. Please don’t forget to follow me:
Subscribe to my newsletter
Read articles from Nitin Saini directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Nitin Saini
Nitin Saini
A Full Stack Web Developer, possessing a strong command of React.js, Node.js, Express.js, MongoDB, and AWS, alongside Next.js, Redux, and modern JavaScript (ES6+)