Xây dựng ứng dụng Todo List bằng HTML, CSS và JavaScript thuần

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Todo List là bài tập "kinh điển" cho người mới học web: nhỏ gọn nhưng chạm đến gần như mọi kỹ năng nền tảng như thao tác DOM, xử lý sự kiện và lưu dữ liệu. Trong bài này bạn sẽ tự tay viết một ứng dụng thêm, đánh dấu hoàn thành và xóa công việc chỉ với một file HTML, không cần cài thêm gì.

Bạn sẽ học được gì

Sau khi làm xong, bạn sẽ nắm được cách chọn phần tử bằng document.querySelector, tạo phần tử mới, lắng nghe sự kiện submit và click, cũng như lưu danh sách vào localStorage để tải lại trang vẫn còn dữ liệu. Đây là những mảnh ghép bạn sẽ dùng lại trong hầu hết dự án front-end sau này.

  • Thêm công việc mới bằng form.
  • Bấm vào công việc để đánh dấu đã hoàn thành.
  • Xóa công việc bằng nút nhỏ bên cạnh.
  • Tự động lưu và khôi phục dữ liệu khi mở lại trình duyệt.

Bước 1: Khung HTML

Tạo file index.html bằng VS Code hoặc bất kỳ trình soạn thảo nào. Chúng ta chỉ cần một form có ô nhập, một nút thêm và một danh sách trống.

<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Todo List</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="app">
    <h1>Việc cần làm</h1>
    <form id="todo-form">
      <input id="todo-input" type="text" placeholder="Nhập công việc..." required>
      <button type="submit">Thêm</button>
    </form>
    <ul id="todo-list"></ul>
  </main>
  <script src="app.js"></script>
</body>
</html>

Bước 2: Thêm chút CSS

Giao diện không cần cầu kỳ, nhưng một chút CSS giúp bạn thấy rõ trạng thái hoàn thành. Tạo file style.css cùng thư mục với nội dung dưới đây.

body { font-family: system-ui, sans-serif; background: #f4f6fb; }
.app { max-width: 420px; margin: 40px auto; background: #fff; padding: 24px; border-radius: 12px; }
form { display: flex; gap: 8px; }
input { flex: 1; padding: 8px; }
li { display: flex; justify-content: space-between; padding: 8px 0; cursor: pointer; }
li.done span { text-decoration: line-through; color: #888; }

Bước 3: Viết logic JavaScript

Ý tưởng chính: giữ danh sách công việc trong một mảng todos, mỗi lần mảng thay đổi thì lưu lại và vẽ lại giao diện bằng hàm render(). Cách tổ chức "dữ liệu trước, giao diện sau" này giúp code dễ hiểu và dễ sửa lỗi. Tạo file app.js:

const form = document.querySelector('#todo-form');
const input = document.querySelector('#todo-input');
const list = document.querySelector('#todo-list');

let todos = JSON.parse(localStorage.getItem('todos')) || [];

function save() {
  localStorage.setItem('todos', JSON.stringify(todos));
}

function render() {
  list.innerHTML = '';
  todos.forEach((todo, index) => {
    const li = document.createElement('li');
    if (todo.done) li.classList.add('done');

    const span = document.createElement('span');
    span.textContent = todo.text;

    const del = document.createElement('button');
    del.textContent = 'Xóa';
    del.addEventListener('click', (e) => {
      e.stopPropagation();
      todos.splice(index, 1);
      save();
      render();
    });

    li.addEventListener('click', () => {
      todo.done = !todo.done;
      save();
      render();
    });

    li.append(span, del);
    list.appendChild(li);
  });
}

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const text = input.value.trim();
  if (!text) return;
  todos.push({ text, done: false });
  input.value = '';
  save();
  render();
});

render();

Lưu ý ta dùng textContent thay vì innerHTML khi hiển thị nội dung người dùng nhập. Đây là thói quen tốt để tránh lỗi chèn mã độc (XSS) ngay từ những dự án đầu tiên.

Bước 4: Chạy thử và kiểm tra

Mở index.html bằng trình duyệt. Thử thêm vài công việc, bấm để gạch bỏ, xóa một mục rồi tải lại trang. Nếu dữ liệu vẫn còn, bạn đã làm đúng. Nếu có lỗi, mở DevTools bằng phím F12, xem tab Console để đọc thông báo và dòng code gây lỗi.

Gợi ý mở rộng

  • Thêm bộ lọc "Tất cả / Đang làm / Hoàn thành".
  • Cho phép sửa nội dung công việc bằng cách nhấp đúp.
  • Hiển thị số công việc còn lại.
  • Viết lại bằng React hoặc Vue để so sánh cách tiếp cận.

Kết luận

Chỉ với khoảng 50 dòng JavaScript, bạn đã có một ứng dụng thật sự chạy được và dùng lại được. Hãy thử tự thêm một tính năng mà không nhìn hướng dẫn, đó là cách nhanh nhất để kiến thức thấm vào đầu.

Nếu muốn đi từ HTML, CSS đến JavaScript theo lộ trình bài bản với nhiều dự án thực hành, bạn có thể tham khảo các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment