jquery event
Kiến thức

Chương 3 – Sự kiện

Có một cách cho phép nhiều phần tử phản ứng lại với một click được gọi là Event Capturing. Với Event Capturing, thì sự kiện được gửi tới phần tử chung nhất sau đó nó đi dần vào những phần tử cụ thể hơn. Ở ví dụ của chúng ta, thì sự kiện sẽ chạy qua thẻ div, sau đó đến span và cuối cùng là thẻ a.

jquery event

Cách đối lập với cách trên được gọi là Even Bubbling (sự kiện bong bóng :-s). Cái này bạn tưởng tượng như trong bể cả trong nhà khi sủi nước vậy. Nước sủi ở dưới, nó bong bóng từ dưới đấy hồ lên trên mặt nước. Sự kiện được gửi tới thành phần cụ thể nhất, và sau khi phần tử này đã có cơ hội để phản ứng, sự kiện sẽ “thổi bong bóng” lên những thành phần chung hơn. Trong ví dụ của chúng ta thì thẻ a sẽ xử lý sự kiện trước, sau đó là thẻ span và div là cuối cùng.

Chẳng có gì là ngạc nhiên khi những người phát triển trình duyệt quyết định những mô hình khác nhau cho sự lan truyền sự kiện. Hệ thống DOM tiêu chuẩn sau này mới dần được phát triển thì định nghĩa rằng cả hai cách trên nên là như sau: đầu tiên sự kiện bị “bắt” bởi những thành phần chung nhất rồi mới đến những phần tử cụ thể hơn, sau đó sự kiện sẽ được “nổi bong bóng” lên trên đỉnh của cây DOM. Bộ xử lý sự kiện có thể được đăng ký ở một trong hai quá trình trên.

Tiếc là không phải toàn bộ các trình duyệt đều áp dụng tiêu chuẩn mới này, và ở những trình duyệt có hỗ trợ Capture thì người ta phải tự tay bật tính năng đó. Để tương thích với mọi trình duyệt, jQuery luôn đăng ký bộ xử lý sự kiện trong quá trình “bong bóng” của mô hình. Chúng ta có thể hiểu rằng, phần tử cụ thể nhất sẽ có cơ hội đầu tiên để phản ứng lại với một sự kiện.

#Phản ứng phụ của sự kiên bong bóng

Event Bubbling có thể tạo ra những biểu hiện không mong đợi, đặc biệt là một thành phần không chủ ý nào đó phản ứng lại với chuột của người dùng. Ví dụ bộ xử lý sự kiện MouseOut được gán vào thẻ div trong ví dụ của chúng ta. Khi chuột của người dùng di chuyển ra ngoài vùng div, thì bộ xử lý MouseOut sẽ được chạy như mong đợi. Bởi vì đây là tầng cao nhất của nấc thang cho nên không thành phần nào khác được “nhìn thấy” sự kiện. Mặt khác, khi trỏ chuột di chuyển ra ngoài phần tử a, một sự kiện mouseout sẽ được gửi đến nó. Sự kiện này sẽ “nổi bong bóng” lên đến thẻ span và sau đó là thẻ div, và kích hoạt cùng một bộ xử lý sự kiện. Dãy sự kiện bong bóng này có thể không được mong đợi trong ví dụ về nút thay đổi kiểu dáng của chúng ta và hiệu ứng tô màu cho đường link có thể bị tắt quá sớm.

Phương thức .hover() hiểu rõ những vấn đề liên quan đến sự kiện bong bóng, và khi chúng ta sử dụng phương thức này để gán sự kiện. Chúng ta có thể bỏ qua những vấn đề tạo ra bởi những thành phần không mong đợưc sẽ phản ứng với sự kiện Mouse over và Mouse Out. Cho nên điều này làm cho phương pháp .hover() là một lựa chọn thích hợp để gán cho mỗi sự kiện liên quan đến chuột.

Lưu ý: Nếu bạn chỉ quan tâm đến khi người dùng di chuột qua hoặc thoát ra khỏi phần tử, mà không phải cả hai, bạn có thể gán sự kiện mouseentermouseleave của jQuery. Cách này cũng tránh được sự kiên bong bóng. Nhưng bởi vì sự kiện liên quan đến chuột thường đi với nhau, cho nên phương pháp .hover() thường là lựa chọn đúng dắn.

Kịch bản Mouse out vừa được nêu ra ở trên cho thấy chúng ta cần phải giới hạn phạm vi của một sự kiện. Trong khi .hover() xử lý tốt trường hợp này, nhưng cũng có trường hợp chúng ta cần phải giới hạn một sự kiện không để nó được gửi tới một thành phần cụ thể khác hoặc tạm thời giới hạn một sự kiện không bị gửi trong bao nhiêu lần.

#Thay đổi đường đi: đối tượng sự kiện

Chúng ta đã thấy một trường hợp mà Event Bubbling có thể tạo ra rắc rối. Chúng ta hãy cùng khám phá một trường hợp mà ở đó .hover() không giúp ích được gì. Chúng ta sẽ sửa lại đoạn code làm sụp nút thay đổi trạng thái ở trên. Giả sử chúng ta muốn mở rộng phạm vi vùng có thể nhấp chuột để kích hoạt hiệu ứng thu nhỏ hoặc mở rộng bộ nút thay đổi định dạng. Cách để thực hiện là di chuyển bộ xử lý sự kiện từ phần label h3 sang phần tử chứa nó là div

$('#switcher').click(function() {
                $('#switcher .button').toggleClass('hidden')
            });

Sau khi bạn bỏ thẻ h3 ở phần Selector đi thì bây giờ nếu bạn click vào bất cứ chỗ nào trong phạm vi của bộ nút cũng sẽ làm ẩn hiện nó. Nhưng vấn đề ở đây là kể cả khi bạn nhấp chuột vào bất cứ nút nào, tuy nó vẫn thay đổi màu sắc như mong muốn, những nó lại đóng lại. Đây chính là hiệu ứng bong bóng, sự kiện ban đầu được xử lý bởi các nút. Sau đó nó được truyền lên cây DOM cho đến khi nó chạm tới <div id=’switcher’>, ở đó bộ xử lý mới được kích hoạt và ẩn hết các nút đi.

Để giải quyết vấn đề này chúng ta phải truy cấp đến đối tượng sự kiện. Đây là cấu trúc của JavaScript được truyền qua mỗi bộ xử lý sự kiện của từng phần tử mỗi khi nó được kích hoạt. Nó cung cấp thông tin về sự kiện đó như là vị trí của con trỏ chuột tại thời điểm của sự kiện. Nó cũng cung cấp một số phương pháp có thể được sử dụng để tạo ảnh hưởng đến quá trình của một sự kiện thông qua DOM.

Để sử dụng đối tượng sự kiện trong bộ xử lý, chúng ta chỉ cần thêm một tham số vào hàm:

$('#switcher').click(function(event) {
                $('#switcher .button').toggleClass('hidden')
            });

#Đích sự kiện (Event Target)

Bây giờ chúng ta đã có đối tượng sự kiện như là một biến sự kiện trong bộ xử lý của chúng ta. Tính năng của đích sự kiện rất hữu dụng trong việc quản lý một sự kiện sẽ được xảy ra ở đâu. Tính năng này là một phần của DOM API (giao diện lập trình ứng dụng), nhưng không được cài đặt ở mọi trình duyệt. Với .target, chúng ta có thể xác lập phần tử nào sẽ nhận sự kiện đầu tiên nhất. Trong trường hợp này là một Click Event, đối tượng chính được nhấp chuột. Hãy nhớ rằng nó cho chúng ta một phần tử DOM để xử lý sự kiện, cho nên chúng ta có thể viết như sau:

	$('#switcher').click(function(event) {
	if(event.target == this) {
                $('#switcher .button').toggleClass('hidden')
	}

Đoạn code này đảm bảo rằng đối tượng được click vào chỉ là <div id=’switcher’>, chứ không phải là các phần tử phụ của nó. Bây giờ khi bạn nhấp chuột vào các nút sẽ không làm ẩn bộ chuyển đi mà nhấn vào vùng nền xung quanh sẽ làm ẩn nó đi. Nhưng nếu bạn nhấn vào nhãn của bộ chuyển là thẻ h3 thì lại không có gì xảy ra bởi vì nó cũng là phần tử con. Chúng ta có thể thay đổi cách xử lý của các nút để đạt được mục tiêu.

#Ngăn chạn sự lan truyền sự kiện

Đối tượng sự kiên cung cấp phương pháp .stopPropagation(), có thể được sử dụng để ngăn chạn hoàn toàn quá trình bong bóng cho sự kiện. Giống như .target, phương pháp này là một tính năng thuần JavaScript, nhưng không tương thích với mọi trình duyệt. Miễn là khi chúng ta đã đăng ký tất cả những bộ xử lý sự kiện sử dụng jQuery, chúng ta có thể sử dụng nó mà không sợ bị lỗi.

Chúng ta sẽ loại bỏ event.target == this ở trên đi và thay vào đó là một ít mã cho các nút:

	$('#switcher .button').click(function(event) {
		  $('body').removeClass();
		  if (this.id == 'switcher-red') {
		      $('body').addClass('red');
		  } else if (this.id == 'switcher-green') {
		      $('body').addClass('green');
		  }
            $('#switcher .button').removeClass('selected');
            $(this).addClass('selected');
	event.stopPropagation();

Như ở trên chúng ta cũng cần phải thêm một tham số vào hàm mà chúng ta đang sử dụng để xử lý click, để chúng ta có được quyền vào đối tượng sự kiện. Sau đó chúng ta chỉ việc gọi event.stopPropagation() để ngăn chặn các phần tử DOM khác không phản ứng lại sự kiện. Bây giờ khi bạn nhấp chuột, thì chỉ có các nút là xử lý sự kiện đó, và chỉ có các nút thôi, nếu nhấp chuột ra các vùng xunh quanh nó sẽ ẩn hoặc hiện bộ nút.

#Tác dụng mặc định

Nếu bộ xử lý sự kiện click của chúng ta được đăng ký cho một phần tử <a> thay vì một thẻ <div>, chúng ta có thể gặp rắc rối. Khi người dùng nhấp chuột vào đường link, trình duyệt sẽ load một trang web mới. Đây không phải là hiệu ứng bong bóng mà chúng ta đã thảo luận ở trên, mà đây chính là tác dụng mặc định cho lần nhấp chuột vào đường liên kết. Cũng giống như khi phím Enter được nhấn khi người dùng đagn điền form, sự kiện submit sẽ được kích hoạt và form sẽ được submit.

Nếu những tác dụng mặc định này không phải điều bạn muốn và bạn gọi .stopPrpagation() nó cũng không có tác dụng gì. Những tác dụng này chẳng xảy ra ở chỗ nào của sự lan truyền sự kiện. Thay vào đó, phương pháp .preventDefault() sẽ ngăn chặn sự kiện ngay tại thời điểm trước khi tác dụng mặc định được kích hoạt.

Lưu ý: Gọi .preventDefault() thường chỉ hữu dụng khi chúng ta đã kiểm tra môi trường của sự kiện. Ví dụ trong khi điền form, chúng ta muón kiểm tra tất cả các trường bắt buộc phải được điền đầy đủ, và chỉ ngăn chặn tác dụng mặc định nếu nó chưa được điền. Chúng ta sẽ học thêm về phần này ở các bài sau

Sự lan truyền sự kiện và tác dụng mặc định là hai chế tài độc lập, một trong hai có thể được ngăn chặn trong khi cái khác vẫn xảy ra. Nếu chúng ta muốn ngăn chặn cả hai, chúng ta có thể return false ngay trong bộ xử lý sự kiện của chúng ta, đó cũng chính là đường tắt để gọi cả hai .stopPropagation().preventDefault cho sự kiện.

#Uỷ thác sự kiện (Event Delegation)

Event Bubbling không phải lúc nào cũng gây ra trở ngại, chúng ta cũng có thể sử dụng nó để làm những việc có ích. Một kỹ thuật rất hay tận dụng bong bóng sự kiện được gọi là uỷ thác sự kiện. Khi dùng chúng ta có thể sử dụng một bộ xử lý sự kiện trên một phần tử đơn và áp dụng lên nhiều phần tử khác.

Lưu Ý: trong jQuery 1.3 một cặp phương pháp được giới thiệu là .live().die(). Hai phương pháp này làm việc giống nhứ .bind().unbind(). Nhưng đằng sau nó, người ta sử dụng uỷ thác sự kiện để làm những việc có ích mà chúng ta sẽ bàn thêm ở phần này.

Trong ví dụ của chúng ta, chúng ta chỉ có ba thẻ <div class=’button’> được gán bộ xử lý nhấp chuột. Nhưng giả sử chúng ta có rất nhiều thẻ div thì sao? Việc này xảy ra nhiều hơn bạn tưởng. Hãy tưởng tượng nếu chúng ta có một bảng và rất nhiều dòng, mỗi dòng có một phần cần có bộ xử lý nhấp chuột. Vòng lặp ẩn có thể thể gán bộ xử lý sự kiện nhắp chuột dễ dàng, nhưng nó có thể làm mã của bạn hoạt động kém hiệu quả bởi vì vòng lặp được tạo ra bên trong jQuery để thao tác với các bộ xử lý kia.

Thay vào đó, chúng ta có thể gắn một bộ xử lý nhấp chuột cho một phần tử gốc trong DOM. Một sự kiện nhấp chuột không gián đoạn sẽ dần dần được gửi tới các thành phần con do hiệu ứng bong bóng sự kiện và chúng ta có thể làm việc ở đó.

Để minh hoạ, chúng ta hãy áp dụng kỹ thuật này vào bộ thay đổi định dạng ở trên, cho dù số lượng các phần tử không nhiều để mà làm cách này, nhưng cũng đủ để chứng minh được cho bạn. Như đã thấy ở trên, chúng ta có thể sử dụng tính năng event.target để xác định thành phần nào đang nằm dưới con trỏ chuột khi người dùng nhấp chuột.

$('#switcher .button').click(function(event) {
		if($(event.target).is('.button')) {
		  $('body').removeClass();
		  if (this.id == 'switcher-red') {
		      $('body').addClass('red');
		  } else if (this.id == 'switcher-green') {
		      $('body').addClass('green');
		  }
            $('#switcher .button').removeClass('selected');
            $(this).addClass('selected');
			event.stopPropagation();
			}

Chúng ta vừa sử dụng một phương pháp mới được gọi là .is(). Phương pháp này chấp nhận những Selector chúng ta vừa học ở phần trước và kiểm tra đối tượng jQuery hiện tại với Selector. Nếu có ít nhất một phần tử phù hợp với Selector thì .is() sẽ trả về giá trị True. Trong ví dụ này, $(event.target).is(‘.button’) hỏi xem thành phần được nhấp chuột có class nào gán cho nó không. Nếu có, thì hãy tiếp tục mã từ đó, với một thay đổi rất lớn: từ khoá this bây giờ nói đến <div id=’switcher’>, cho nên mỗi khi chúng ta quan tâm đến nút được nhấp chuột, chúng ta phải gọi nó với event.target.

Lưu ý: Chúng ta cũng có thể kiểm tra sự tồn tại của một class trên một phần tử với phương pháp ngắn hơn là .hasClass(). Nhưng phương pháp .is() thì linh động hơn và có thể kiểm tra bất cứ Selector nào.

Tuy nhiên chúng ta vẫn có một hiệu ứng phụ không mong đợi trong đoạn mã trên. Khi chúng ta nhấp chuột vào một nút, cả nhóm sẽ bị đóng lại như trước khi chúng ta gọi .stopPropagation(). Bộ xử lý cho việc ẩn hiện bộ nút bây giờ được gán cho thành phần giống như các nút, cho nên nếu bạn chỉ ngăn chặn bong bóng sự kiện sẽ không chặn được việc thay đổi class bị kích hoạt. Để khắc phục vấn đề này, chúng ta sẽ loại bỏ .stopPropagation() và thay vào đó là một phương pháp .is() nữa.

$(document).ready(function() {
            $('#switcher').click(function(event) {
			if (!$(event.target).is('.button'))	{
                $('#switcher .button').toggleClass('hidden');
				}
            }); 
		$('#switcher .button').click(function(event) {
		if($(event.target).is('.button')) {
		  $('body').removeClass();
		  if (this.id == 'switcher-red') {
		      $('body').addClass('red');
		  } else if (this.id == 'switcher-green') {
		      $('body').addClass('green');
		  }
            $('#switcher .button').removeClass('selected');
            $(this).addClass('selected');
			}
		});  
        });

Thực tế thì ví dụ này hơi bị phức tạp hoá, nhưng khi số thành phần và bộ xử lý sự kiện tăng lên, thì uỷ thác sự kiện là cách mà bạn nên dùng.

Lưu ý: Uỷ thác sự kiện cũng hữu dụng trong các trường hợp khác mà chúng ta sẽ học sau này như là khi thêm vào các phần tử bởi phương pháp DOM Manipulation hoặc sử dụng AJAX.

#Loại bỏ một bộ xử lý sự kiện

Có những lúc chúng ta đã sử dụng xong một bộ xử lý sự kiện mà đã được đăng ký từ trước. Ví dụ như trạng thái của trang đã thay đổi mà sự kiện đó không còn phù hợp nữa. Bạn cũng có thể sử dụng những mệnh đề có điều kiện để xử lý tình huống này, nhưng cách hay hơn có thể là hoàn toàn gỡ bỏ (unbind) bộ xử lý đó.

Giả sử chúng ta muốn bộ nút thay đổi kiểu dáng vẫn được mở rộng bất cứ khi nào trang web không sử dụng trạng thái mặc định. Khi mà nút Màu Xanh và Màu Đỏ được nhấn, thì bộ nút sẽ không bị thay đổi gì khi người dùng nhấp chuột xung quanh nó. Chúng ta có thể làm được việc này bằng cách sử dụng phương pháp .unbind() để loại bỏ bộ xử lý đóng lại bộ nút khi mà một trong những nút bấm không phải là nút mặc định được nhấp chuột.

$(document).ready(function() {
            $('#switcher').click(function(event) {
	if (!$(event.target).is('.button'))	{
                $('#switcher .button').toggleClass('hidden');
	}
            }); 
	$('#switcher-red, #switcher-green').click(function() {
	$('#switcher').unbind('click');
	});
 });

Bây giờ khi bạn nhấp chuột vào nút Màu Xanh hoặc Màu Đỏ, thì bộ xử lý nhấp chuột ở thẻ div mẹ sẽ bị loại bỏ, cho nên khi nhấp chuột vào vùng xunh quanh của hộp sẽ không làm ẩn bộ nút đi. Nhưng hệ thống nút của chúng ta cũng không làm việc nữa. Bởi vì nó cũng được gán với bộ xử lý nhấp chuột của thẻ <div> mẹ do chúng ta viết lại để sử dụng uỷ thác sự kiện. Cho nên khi chúng ta gọi $(‘#switcher’).unbind(‘click’), cả hai cách xử lý đều bị loại bỏ.

Related posts

Thuật ngữ chuyên ngành bạn cần biết

admin

Giới thiệu về jQuery

admin

Cách viết bài cho đối tượng đọc lướt

admin

Leave a Comment