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

Nitin SainiNitin Saini
4 min read

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:

Twitter
LinkedIn

0
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+)