Practice: Building an Interactive To-Do List with DOM

Creating a to-do list is one of those beginner-friendly JavaScript projects that teaches you how to work with the DOM in a real, useful way. You’ll create elements with JavaScript, add them to the page, respond to user actions like clicks, and remove elements dynamically. It’s a perfect hands-on way to understand how everything connects.

Add tasks

To start, we need a way for the user to type a task and add it to the list.

What we need:

  • An input box for entering tasks.
  • A button to add the task.
  • A place to show the list (a <ul> element).

Here’s how we’ll do it in JavaScript:

javascript
1
2
3
          const taskInput = document.getElementById("taskInput");
const addTaskBtn = document.getElementById("addTaskBtn");
const taskList = document.getElementById("taskList");
        

We grab the input field, the button, and the task list from the DOM using getElementById.

Then we listen for a click on the "Add" button:

javascript
1
2
3
4
          addTaskBtn.addEventListener("click", () => {
  const taskText = taskInput.value.trim();
  if (taskText === "") return;
});
        

We get the text from the input, remove extra spaces, and ignore empty entries.

Now we create a new task item:

javascript
1
2
3
          const li = document.createElement("li");
const taskSpan = document.createElement("span");
taskSpan.textContent = taskText;
        
  • li is the list item.
  • taskSpan holds the text.

We attach them like this:

javascript
1
2
          li.appendChild(taskSpan);
taskList.appendChild(li);
        

Now the task shows up in the list when the button is clicked.

Mark tasks as completed

We want users to click on a task to mark it done. We do this by toggling a class when the text is clicked:

javascript
1
2
3
          taskSpan.addEventListener("click", () => {
  li.classList.toggle("completed");
});
        

In our CSS, this .completed class applies a line-through style. Simple, clear feedback.

Delete tasks

We also need a way to remove tasks. Each task will get a small "Delete" button.

Here’s the code:

javascript
1
2
3
4
5
6
7
          const deleteBtn = document.createElement("button");
deleteBtn.textContent = "Delete";
deleteBtn.className = "delete-btn";

deleteBtn.addEventListener("click", () => {
  li.remove();
});
        

This creates a button, gives it some text and a class, and removes the entire li when clicked.

Then we attach the delete button:

javascript
1
          li.appendChild(deleteBtn);
        

Clear the input

After adding a task, we want to reset the input so the user can type a new one:

javascript
1
2
          taskInput.value = "";
taskInput.focus();
        

Final Code (HTML, CSS, JavaScript)

HTML

html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
          <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="description" content="Learn DOM manipulation by building a simple interactive to-do list with HTML, CSS, and JavaScript." />
  <title>To-Do List Practice</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <div class="container">
    <h1>My To-Do List</h1>
    <div class="input-group">
      <input type="text" id="taskInput" placeholder="Add a new task..." />
      <button id="addTaskBtn">Add</button>
    </div>
    <ul id="taskList"></ul>
  </div>

  <script src="script.js"></script>
</body>
</html>
        

CSS (style.css)

css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
          * {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
  background: #f4f4f4;
  padding: 40px;
  display: flex;
  justify-content: center;
}

.container {
  background: white;
  padding: 20px 30px;
  border-radius: 8px;
  width: 100%;
  max-width: 400px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

h1 {
  text-align: center;
  margin-bottom: 20px;
}

.input-group {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

#taskInput {
  flex: 1;
  padding: 10px;
  font-size: 16px;
}

#addTaskBtn {
  padding: 10px 16px;
  font-size: 16px;
  background: #007bff;
  color: white;
  border: none;
  cursor: pointer;
  border-radius: 4px;
}

#addTaskBtn:hover {
  background: #0056b3;
}

#taskList {
  list-style: none;
}

li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #f9f9f9;
  padding: 10px;
  margin-bottom: 8px;
  border-radius: 4px;
  transition: background 0.2s;
}

li.completed span {
  text-decoration: line-through;
  color: #888;
}

.delete-btn {
  background: transparent;
  border: none;
  color: #dc3545;
  font-weight: bold;
  cursor: pointer;
}
        

JavaScript (script.js)

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
          const taskInput = document.getElementById("taskInput");
const addTaskBtn = document.getElementById("addTaskBtn");
const taskList = document.getElementById("taskList");

addTaskBtn.addEventListener("click", () => {
  const taskText = taskInput.value.trim();
  if (taskText === "") return;

  const li = document.createElement("li");

  const taskSpan = document.createElement("span");
  taskSpan.textContent = taskText;

  taskSpan.addEventListener("click", () => {
    li.classList.toggle("completed");
  });

  const deleteBtn = document.createElement("button");
  deleteBtn.textContent = "Delete";
  deleteBtn.className = "delete-btn";

  deleteBtn.addEventListener("click", () => {
    li.remove();
  });

  li.appendChild(taskSpan);
  li.appendChild(deleteBtn);
  taskList.appendChild(li);

  taskInput.value = "";
  taskInput.focus();
});