Sự kiện trong JavaScript

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

  1. Nguồn sự kiện (ai)
  2. Loại sự kiện (sự kiện gì)
  3. 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 ý:

  1. Mã JavaScript chỉ có thể thực thi một trong hai giai đoạn bắt hoặc nổi bọt.
  2. onclick và attachEvent chỉ nhận được giai đoạn nổi bọt.
  3. 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.
  4. 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.
  5. Một số sự kiện không có nổi bọt, ví dụ onblur, onfocus, onmouseenter, onmouseleave
  6. 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ụ:

  1. Phần tử nào đã gắn kết sự kiện này.
  2. 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.
  3. 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:

  1. 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.
  2. 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.

  1. Cách viết chuẩn: Sử dụng phương thức stopPropagation() trong đối tượng sự kiện
  2. e.stopPropagation()
  3. 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
  4. 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

  1. Ngăn chặn menu chuột phải
  2. 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();
    })
  3. Ngăn chặn việc chọn chuột (selectstart bắt đầu chọn)
  4. 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:

  1. Chuột liên tục di chuyển, sử dụng sự kiện di chuột: mousemove
  2. Di chuyển trong trang, đăng ký sự kiện cho document
  3. 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
  4. 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 ý:

  1. Nếu sử dụng addEventListener không cần thêm on
  2. 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.
  3. 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:

  1. Ý 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
  2. 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
  3. 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.)

Thẻ: JavaScript DOM Event Handling Event delegation Mouse events

Đăng vào ngày 29 tháng 6 lúc 13:50