Nguyễn Đình Cường
0 comment
09 Nov, 2026
Giao diện web chỉ thật sự "sống" khi nó phản hồi hành động của người dùng: bấm nút, gõ phím, cuộn trang, gửi form. Trong JavaScript, những hành động này gọi là sự kiện (event). Hiểu cách lắng nghe sự kiện và cơ chế nổi bọt (bubbling) sẽ giúp bạn viết mã gọn, hiệu quả và ít lỗi.
const nut = document.querySelector('#dem');
let dem = 0;
nut.addEventListener('click', function (event) {
dem++;
nut.textContent = 'Đã bấm ' + dem + ' lần';
});addEventListener nhận tên sự kiện và một hàm xử lý. Hàm đó nhận đối tượng event chứa thông tin, trong đó event.target là phần tử gốc phát sinh sự kiện. Một phần tử có thể có nhiều trình lắng nghe cho cùng một sự kiện, khác với cách gán thuộc tính onclick chỉ giữ được một. Muốn gỡ trình lắng nghe, dùng removeEventListener với cùng tham chiếu hàm.
click, dblclick: nhấp chuột.input: giá trị ô nhập thay đổi mỗi khi gõ. change: thay đổi khi rời ô.submit: form được gửi.keydown, keyup: phím bấm.scroll, resize: cuộn và đổi kích thước.mouseenter, mouseleave: rê chuột vào và ra.focus, blur: nhận và mất focus.Một số sự kiện có hành vi mặc định, như form tải lại trang khi gửi, hay liên kết chuyển trang. Bạn ngăn chúng bằng preventDefault():
const form = document.querySelector('#dang-ky');
form.addEventListener('submit', function (e) {
e.preventDefault();
const email = form.elements.email.value.trim();
if (!email) {
alert('Vui lòng nhập email');
return;
}
console.log('Gửi email:', email);
});Hãy lắng nghe submit trên form thay vì click trên nút, để cả việc nhấn Enter trong ô nhập cũng được xử lý.
Khi bạn bấm vào một phần tử, sự kiện được kích hoạt ở phần tử đó trước, rồi "nổi bọt" lên cha, ông, và cứ thế đến cả tài liệu. Nhờ vậy bạn có thể đặt một trình lắng nghe ở phần tử cha để nghe sự kiện của mọi con bên trong. Nếu cần, event.stopPropagation() dừng việc nổi lên, nhưng nên hạn chế vì nó có thể làm hỏng các trình lắng nghe ở chỗ khác.
Giả sử bạn có danh sách công việc, mỗi mục có nút xóa, và các mục được thêm động. Gắn trình lắng nghe cho từng nút vừa tốn vừa không áp dụng cho mục thêm sau. Giải pháp là ủy quyền cho phần tử cha:
const danhSach = document.querySelector('#todo-list');
danhSach.addEventListener('click', function (e) {
const nutXoa = e.target.closest('.xoa');
if (!nutXoa) return; // bấm ở chỗ khác, bỏ qua
nutXoa.closest('li').remove();
});Chỉ một trình lắng nghe duy nhất, hoạt động cho cả mục có sẵn lẫn mục thêm sau này. Phương thức closest giúp tìm đúng nút kể cả khi người dùng bấm vào icon nằm bên trong nút.
Sự kiện như scroll, resize và input có thể kích hoạt hàng chục lần mỗi giây. Hai kỹ thuật phổ biến là:
function debounce(fn, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const timKiem = debounce((e) => console.log('Tìm:', e.target.value), 300);
document.querySelector('#q').addEventListener('input', timKiem);Sự kiện bàn phím cho phép bạn thêm phím tắt, nhưng nên làm cẩn thận để không xung đột với người đang gõ vào ô nhập:
document.addEventListener('keydown', function (e) {
const dangGo = ['INPUT', 'TEXTAREA'].includes(e.target.tagName);
if (dangGo) return;
if (e.key === '/') {
e.preventDefault();
document.querySelector('#q').focus();
}
if (e.key === 'Escape') {
document.querySelector('.modal')?.classList.remove('open');
}
});Hãy dùng e.key (như 'Enter', 'Escape') thay vì mã phím số kiểu cũ vì dễ đọc hơn. Đừng quên rằng phím tắt không thay thế cho các nút bấm trên giao diện, chỉ là lối tắt cho người dùng quen tay.
Tham số thứ ba của addEventListener có thể là một đối tượng cấu hình với vài tùy chọn đáng biết:
{ once: true }: trình lắng nghe tự gỡ sau lần chạy đầu tiên, tiện cho hành động chỉ làm một lần.{ passive: true }: cam kết không gọi preventDefault(), giúp trình duyệt cuộn mượt hơn với sự kiện cảm ứng và cuộn.{ signal }: truyền tín hiệu từ AbortController để gỡ nhiều trình lắng nghe cùng lúc bằng một lần gọi abort().{ capture: true }: lắng nghe ở giai đoạn bắt (từ trên xuống) thay vì giai đoạn nổi bọt.nut.addEventListener('click', () => console.log('Chỉ chạy một lần'), { once: true });Hãy nhớ bốn ý: dùng addEventListener, tận dụng preventDefault cho form, hiểu bubbling và áp dụng delegation cho danh sách động, và kiểm soát tần suất khi sự kiện dồn dập. Chúng giúp mã của bạn vừa gọn vừa nhanh.
Bạn có thể luyện tập thêm với dự án tương tác trong các khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment