Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
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ì.
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.
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>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; }Ý 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.
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.
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.
Nguyễn Đình Cường
0 comment