Cơ bản về Sự kiện
JavaScript cho phép chúng ta tạo các trang web động, và sự kiện là những hành vi có thể được JavaScript phát hiện. Cách hiểu đơn giản: cơ chế kích hoạt - phản hồi. Mỗi phần tử trong trang web đều có thể tạo ra các sự kiện có thể kích hoạt JavaScript, ví dụ, chúng ta có thể tạo ra một sự kiện khi người dùng nhấp vào một nút, sau đó thực hiện một số thao tác.
Bộ ba thành phần của sự kiện
- Nguồn sự kiện (ai)
- Loại sự kiện (sự kiện gì)
- Chương trình xử lý sự kiện (làm gì)
Ví dụ: Nhấp vào nút hiển thị hộp cảnh báo
//1. Lấy nguồn sự kiện (nút)
//2. Đăng ký sự kiện (gắn kết sự kiện), sử dụng onclick
//3. Viết chương trình xử lý sự kiện, tạo một hàm hiển thị hộp cảnh báo alert
const nut = document.getElementById('nut');
nut.onclick = function() {
alert('Bạn có khỏe không?');
};
Đăng ký sự kiện truyền thống và phương thức lắng nghe
Phương thức lắng nghe sự kiện addEventListener
eventTarget.addEventListener(type, listener[, useCapture])
Phương thức eventTarget.addEventListener() đăng ký trình lắng nghe được chỉ định vào eventTarget (đối tượng mục tiêu), khi đối tượng này kích hoạt sự kiện được chỉ định, hàm xử lý sự kiện sẽ được thực thi. Phương thức này nhận ba tham số:
- type: chuỗi loại sự kiện, ví dụ click, mouseover, lưu ý không có "on" ở đây
- listener: hàm xử lý sự kiện, sẽ được gọi khi sự kiện xảy ra
- useCapture: tham số tùy chọn, là một giá trị boolean, mặc định là false. Chúng ta sẽ tìm hiểu thêm sau khi học về luồng sự kiện DOM
Phương thức lắng nghe sự kiện attachEvent
eventTarget.attachEvent(eventNameWithOn, callback)
Phương thức eventTarget.attachEvent() đăng ký trình lắng nghe được chỉ định vào eventTarget (đối tượng mục tiêu), khi đối tượng này kích hoạt sự kiện được chỉ định, hàm callback được chỉ định sẽ được thực thi. Phương thức này nhận hai tham số:
- eventNameWithOn: chuỗi loại sự kiện, ví dụ onclick, onmouseover, cần có "on" ở đây
- callback: hàm xử lý sự kiện, được gọi khi đối tượng mục tiêu kích hoạt sự kiện
Lưu ý: Hỗ trợ trong IE8 và các phiên bản cũ hơn
<body>
<button>Đăng ký sự kiện truyền thống</button>
<button>Đăng ký sự kiện bằng phương thức lắng nghe</button>
<button>attachEvent cho ie9</button>
<script>
const nutArray = document.querySelectorAll('button');
// 1. Đăng ký sự kiện theo cách truyền thống
nutArray[0].onclick = function() {
alert('xin chào');
}
nutArray[0].onclick = function() {
alert('bạn khỏe không');
}
// 2. Đăng ký sự kiện bằng phương thức lắng nghe addEventListener
// (1) loại sự kiện bên trong là chuỗi phải có dấu ngoặc kép và không có on
// (2) Cùng một phần tử, cùng một sự kiện có thể thêm nhiều trình lắng nghe (chương trình xử lý sự kiện)
nutArray[1].addEventListener('click', function() {
alert(22);
})
nutArray[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent hỗ trợ cho phiên bản trước ie9
nutArray[2].attachEvent('onclick', function() {
alert(11);
})
</script>
</body>
Giải pháp tương thích khi đăng ký sự kiện
function themSuKien(element, eventName, fn) {
// Kiểm tra xem trình duyệt hiện tại có hỗ trợ phương thức addEventListener không
if (element.addEventListener) {
element.addEventListener(eventName, fn); // Tham số thứ ba mặc định là false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// Tương đương với element.onclick = fn;
element['on' + eventName] = fn;
}
}
Nguyên tắc xử lý tương thích: Ưu tiên các trình duyệt phổ biến trước, sau đó xử lý các trình duyệt đặc biệt
Xóa sự kiện (gỡ bỏ sự kiện)
1. Cách đăng ký truyền thống
eventTarget.onclick = null;
2. Cách đăng ký bằng phương thức lắng nghe
eventTarget.removeEventListener(type, listener[, useCapture]);
eventTarget.detachEvent(eventNameWithOn, callback);
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
const divArray = document.querySelectorAll('div');
divArray[0].onclick = function() {
alert(11);
// 1. Xóa sự kiện theo cách truyền thống
divArray[0].onclick = null;
}
// 2. Xóa sự kiện bằng removeEventListener
divArray[1].addEventListener('click', hamXuLy) // hamXuLy không cần gọi với dấu ngoặc đơn
function hamXuLy() {
alert(22);
divArray[1].removeEventListener('click', hamXuLy);
}
// 3. detachEvent
divArray[2].attachEvent('onclick', hamXuLy2);
function hamXuLy2() {
alert(33);
divArray[2].detachEvent('onclick', hamXuLy2);
}
</script>
</body>
Giải pháp tương thích khi xóa sự kiện
function xoaSuKien(element, eventName, fn) {
// Kiểm tra xem trình duyệt hiện tại có hỗ trợ phương thức removeEventListener không
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // Tham số thứ ba mặc định là false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
} else {
element['on' + eventName] = null;
}
}
Luồng sự kiện DOM
Luồng sự kiện mô tả thứ tự nhận sự kiện từ trang. Khi sự kiện xảy ra, nó sẽ lan truyền giữa các nút phần tử theo thứ tự cụ thể, quá trình lan truyền này được gọi là luồng sự kiện DOM. Ví dụ, chúng ta đăng ký sự kiện nhấp vào một div:
Sự kiện nổi bọt: Được IE đề xuất đầu tiên, sự kiện bắt đầu được nhận bởi phần tử cụ thể nhất, sau đó lan truyền dần lên nút DOM ở tầng cao nhất.
Sự kiện bắt: Được Netscape đề xuất đầu tiên, bắt đầu từ nút DOM ở tầng cao nhất, sau đó lan truyền dần xuống phần tử cụ thể nhất.
Chúng ta có thể hình dung như sau: khi ném một hòn đá vào nước, đầu tiên nó có một quá trình đi xuống, quá trình này có thể hiểu là quá trình bắt từ tầng cao nhất đến phần tử cụ thể nhất (điểm mục tiêu); sau đó sẽ tạo ra bọt, bọt sẽ nổi lên từ điểm thấp nhất (phần tử cụ thể nhất) đến bề mặt nước, quá trình này tương đương với sự kiện nổi bọt.
Lưu ý:
- Mã JavaScript chỉ có thể thực thi một trong hai giai đoạn bắt hoặc nổi bọt.
- onclick và attachEvent chỉ nhận được giai đoạn nổi bọt.
- Tham số thứ ba của addEventListener(type, listener[, useCapture]) nếu là true, biểu thị gọi chương trình xử lý sự kiện ở giai đoạn bắt; nếu là false (không viết mặc định là false), biểu thị gọi chương trình xử lý sự kiện ở giai đoạn nổi bọt.
- Trong thực tế, chúng ta hiếm khi sử dụng sự kiện bắt, chúng ta tập trung nhiều hơn vào sự kiện nổi bọt.
- Một số sự kiện không có nổi bọt, ví dụ onblur, onfocus, onmouseenter, onmouseleave
- Sự kiện nổi bọt đôi khi gây rắc rối, nhưng đôi khi lại giúp chúng ta thực hiện các công việc một cách khéo léo, chúng ta sẽ thảo luận sau.
Đối tượng sự kiện
Đối tượng sự kiện là gì?
eventTarget.onclick = function(event) {}
eventTarget.addEventListener('click', function(event) {})
Trong đoạn mã trên, event này chính là đối tượng sự kiện, chúng ta cũng có thể viết là e hoặc evt. Giải thích chính thức: đối tượng event đại diện cho trạng thái của sự kiện, ví dụ trạng thái phím bàn phím, vị trí chuột, trạng thái nút chuột. Cách hiểu đơn giản: sau khi sự kiện xảy ra, tất cả các tập dữ liệu liên quan đến sự kiện sẽ được đặt vào đối tượng này, đối tượng này chính là đối tượng sự kiện event, nó có nhiều thuộc tính và phương thức.
Ví dụ:
- Phần tử nào đã gắn kết sự kiện này.
- Nếu sự kiện được kích hoạt bởi chuột, sẽ nhận được thông tin liên quan đến chuột, ví dụ vị trí chuột.
- Nếu sự kiện được kích hoạt bởi bàn phím, sẽ nhận được thông tin liên quan đến bàn phím, ví dụ phím nào đã được nhấn.
Cú pháp sử dụng đối tượng sự kiện
eventTarget.onclick = function(event) {
// event này chính là đối tượng sự kiện, chúng ta cũng có thể viết là e hoặc evt
}
eventTarget.addEventListener('click', function(event) {
// event này chính là đối tượng sự kiện, chúng ta cũng có thể viết là e hoặc evt
})
event này là tham số hình thức, hệ thống đã đặt là đối tượng sự kiện, không cần truyền tham số thực. Khi chúng ta đăng ký sự kiện, đối tượng event sẽ được hệ thống tự động tạo và truyền lần lượt cho trình lắng nghe sự kiện (chương trình xử lý sự kiện).
Giải pháp tương thích đối tượng sự kiện
Việc lấy đối tượng sự kiện có vấn đề tương thích:
- Trong các trình duyệt chuẩn, trình duyệt truyền tham số cho phương thức, chỉ cần định nghĩa tham số hình thức e là có thể lấy được.
- Trong IE6~8, trình duyệt không truyền tham số cho phương thức, nếu cần, cần tìm kiếm trong window.event. Giải pháp:
e = e || window.event;
Các thuộc tính và phương thức phổ biến của đối tượng sự kiện
e.target và this khác nhau:
this là phần tử gắn kết sự kiện, người gọi hàm này (phần tử gắn kết sự kiện)
e.target là phần tử kích hoạt sự kiện.
Ngăn chặn sự kiện nổi bọt
Hai cách ngăn chặn sự kiện nổi bọt
Sự kiện nổi bọt: Bắt đầu từ phần tử cụ thể nhất, sau đó lan truyền dần lên nút DOM ở tầng cao nhất. Bản thân sự kiện nổi bọt có thể gây ra những tác hại, nhưng cũng có thể mang lại những lợi ích, chúng ta cần linh hoạt nắm bắt.
- Cách viết chuẩn: Sử dụng phương thức stopPropagation() trong đối tượng sự kiện
- Cách không chuẩn: Sử dụng thuộc tính cancelBubble của đối tượng sự kiện trong IE 6-8
e.stopPropagation()
e.cancelBubble = true;
Giải pháp tương thích ngăn chặn sự kiện nổi bọt
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
Ủy thác sự kiện (ủy quyền, ủy nhiệm)
Nguyên lý của ủy thác sự kiện
Không phải mỗi nút con đặt riêng biệt trình lắng nghe sự kiện, mà trình lắng nghe sự kiện được đặt ở nút cha, sau đó sử dụng nguyên lý nổi bọt để ảnh hưởng đến từng nút con. Ví dụ trên: Đăng ký sự kiện nhấp vào ul, sau đó sử dụng thuộc tính target của đối tượng sự kiện để tìm li đang được nhấp vào hiện tại, vì khi nhấp vào li, sự kiện sẽ nổi bọt lên ul, ul có đăng ký sự kiện, sẽ kích hoạt trình lắng nghe sự kiện.
Tác dụng của ủy thác sự kiện
Chúng ta chỉ thao tác DOM một lần, cải thiện hiệu suất của chương trình.
Ví dụ trong cuộc sống: Lớp có 100 học sinh, nhân viên giao hàng có 100 bưu kiện, nếu giao từng người sẽ tốn nhiều thời gian. Đồng thời, mỗi học sinh nhận hàng cũng cần xếp hàng, cũng tốn nhiều thời gian. Làm thế nào? Giải pháp: Nhân viên giao hàng giao 100 bưu kiện cho giáo viên chủ nhiệm, giáo viên chủ nhiệm đặt các bưu kiện này vào văn phòng, học sinh tự lấy sau giờ học.
Ưu điểm: Nhân viên giao hàng tiết kiệm công sức, giao cho giáo viên chủ nhiệm là xong. Học sinh lấy cũng tiện, vì tin tưởng giáo viên chủ nhiệm.
Ví dụ trong lập trình:
<ul>
<li>Biết chưa biết chưa, nên có hộp thoại</li>
<li>Biết chưa biết chưa, nên có hộp thoại</li>
<li>Biết chưa biết chưa, nên có hộp thoại</li>
<li>Biết chưa biết chưa, nên có hộp thoại</li>
<li>Biết chưa biết chưa, nên có hộp thoại</li>
</ul>
Nhấp vào mỗi li sẽ hiển thị hộp thoại, trước đây cần đăng ký sự kiện cho mỗi li, rất vất vả, và càng truy cập DOM nhiều, điều này sẽ làm chậm thời gian sẵn sàng tương tác của toàn trang.
Các sự kiện chuột phổ biến
| Sự kiện | Giải thích |
|---|---|
| onclick | Kích hoạt khi nhấp chuột trái |
| onmouseover | Kích hoạt khi di chuột qua |
| onmouseout | Kích hoạt khi di chuột ra ngoài |
| onfocus | Kích hoạt khi nhận được tiêu điểm chuột |
| onblur | Kích hoạt khi mất tiêu điểm chuột |
| onmousemove | Kích hoạt khi di chuột |
| onmouseup | Kích hoạt khi nhả chuột |
| onmousedown | Kích hoạt khi nhấn chuột |
Các sự kiện chuột khác
- Ngăn chặn menu chuột phải
- Ngăn chặn việc chọn chuột (selectstart bắt đầu chọn)
contextmenu chủ yếu kiểm soát khi nào hiển thị menu ngữ cảnh, chủ yếu được lập trình viên sử dụng để hủy menu ngữ cảnh mặc định
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
Đối tượng sự kiện chuột
Đối tượng event đại diện cho trạng thái của sự kiện, là tập hợp thông tin liên quan đến sự kiện. Chúng ta chủ yếu sử dụng đối tượng sự kiện chuột MouseEvent và đối tượng sự kiện bàn phím KeyboardEvent.
Ví dụ: Một hình ảnh nhỏ luôn di chuyển theo chuột
Phân tích trường hợp:
- Chuột liên tục di chuyển, sử dụng sự kiện di chuột: mousemove
- Di chuyển trong trang, đăng ký sự kiện cho document
- Hình ảnh cần di chuyển khoảng cách, và không chiếm vị trí, chúng ta sử dụng định vị tuyệt đối
- Nguyên lý cốt lõi: Mỗi lần chuột di chuyển, chúng ta sẽ nhận được tọa độ chuột mới nhất, đặt tọa độ x và y này làm giá trị top và left của hình ảnh để di chuyển
const hinh = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
const x = e.pageX;
const y = e.pageY;
hinh.style.top = y - 40 + 'px';
hinh.style.left = x - 50 + 'px';
})
Các sự kiện bàn phím phổ biến
Sự kiện không chỉ có thể được kích hoạt bằng chuột, mà còn có thể được kích hoạt bằng bàn phím.
Lưu ý:
- Nếu sử dụng addEventListener không cần thêm on
- onkeypress và hai cái trước đó khác nhau ở chỗ, nó không nhận dạng các phím chức năng, ví dụ phím mũi tên trái, shift, v.v.
- Thứ tự thực thi của ba sự kiện là: keydown -- keypress --- keyup
Đối tượng sự kiện bàn phím
Lưu ý: onkeydown và onkeyup không phân biệt chữ hoa chữ thường, onkeypress phân biệt chữ hoa chữ thường. Trong thực tế, chúng ta sử dụng nhiều hơn keydown và keyup, chúng có thể nhận dạng tất cả các phím (bao gồm phím chức năng). Keypress không nhận dạng phím chức năng, nhưng thuộc tính keyCode có thể phân biệt chữ hoa chữ thường, trả về các giá trị ASCII khác nhau.
Ví dụ: Khi nhấn phím s, con trỏ sẽ đặt vào hộp tìm kiếm
Phân tích trường hợp:
- Ý tưởng cốt lõi: Kiểm tra người dùng có nhấn phím s không, nếu nhấn phím s, đặt con trỏ vào hộp tìm kiếm
- Sử dụng thuộc tính keyCode trong đối tượng sự kiện bàn phím để xác định người dùng có nhấn phím s không
- Hộp tìm kiếm nhận tiêu điểm: Sử dụng phương thức focus() của JavaScript
const timKiem = document.querySelector('input');
document.addEventListener('keyup', function(e) {
// console.log(e.keyCode);
if (e.keyCode === 83) {
timKiem.focus();
}
})
Các sự kiện khác thường dùng
| Sự kiện | Giải thích |
|---|---|
| onchange | Sự kiện thay đổi nội dung: Phần tử HTML thay đổi |
| onselect | Sự kiện chọn nội dung: Khi văn bản trong hộp văn bản hoặc vùng văn bản được chọn |
| onkeydown | Người dùng nhấn phím bàn phím |
| onload | Sự kiện tải: Trình duyệt đã hoàn tất tải trang |
| onunload | Sự kiện gỡ tải: Khi người dùng thoát trang (đóng trang, làm mới trang, v.v.) |