Chương 4 – Sự kiện (Events)
JavaScript có một số cách được lập sẵn để phản ứng với những tương tác của người dùng và
những sự kiện khác. Để làm cho trang web năng động và tương tác tốt, chúng ta cần phải tận
dụng chức năng này, để vào những thời điểm phù hợp, chúng ta có thể sử dụng những kỹ
thuật jQuery đã học và sắp học. Bạn cũng có thể làm những việc sau với anh bạn thân
JavaScript, nhưng jQuery nâng cao và mở rộng những cơ chế quản lý sự kiện cơ bản để giúp
nó có cú pháp đẹp hơn, tiết kiệm thời gian hơn và tất nhiên cũng mạnh mẽ hơn.
Thực hiện tác vụ khi trang được load
Chúng ta đã biết cách làm cho jQuery phản ứng như thế nào khi trang web được load. Bộ
quản lý sự kiện $(document).ready() có thể được dùng để kích hoạt một hàm nào đó, nhưng
chúng ta có thể bàn thêm một chút về nó.
Định thời gian thực thi code
Trong chương 1, chúng ta đã biết rằng $(document).ready() là cách của jQuery thực hiện các
tác vụ tương đương với cách mà JavaScript thực hiện tác vụ với onload event được lập sẵn.
Thực tế thì hai cách này đều có tác dụng giống nhau, nhưng chúng lại kích hoạt tác vụ ở
những thời điểm hơi khác nhau.
Sự kiện window.onload được kích hoạt khi mà trình duyệt đã hoàn toàn load xong tài liệu.
Điều này có nghĩa rằng mọi phần tử trên trang đã sẵn sàng để được thao tác bởi JavaScript.
Đây chính là một điểm thuận lợi để chúng ta viết code mà không phải lo lắng về trật tự load.
Mặt khác, bộ quản lý đăng ký sử dụng $(document).ready() được kích hoạt khi DOM hoàn
toàn sẵn sàng để sử dụng. Điều này cũng có nghĩa rằng mọi thành phần có thể được truy cập
bởi code của chúng ta, nhưng không nhât thiết là tài liệu liên quan đã được download. Ngay
sau khi HTML được download và chuyển qua cây DOM, code có thể được thực thi.
Lưu ý: Để đảm bảo rằng trang web vẫn có định dạng trước khi code JavaScript được thực
hiện, người ta thường đặt <link rel=”stylesheet”> đằng trước thẻ <script> trong phần <head>
của tài liệu.
Ví dụ chúng ta có một trang thư viện hình ảnh, trang đó bao gồm nhiều hình có dung lượng
lớn mà chúng ta có thể ẩn, hiện, di chuyển hoặc thao tác với jQuery. Nếu bây giờ chúng ta
thiết lập giao diện sử dụng sự kiện onload, thì người dùng sẽ phải đợi cho đến khi mọi tấm
hình đã được download trước khi họ có thể sử dụng trang web. Hoặc tệ hơn, nếu những cách
xử lý chưa được gán cho các phần tử có cách xử lý mặc định riêng như là các đường liên kết,
thì việc tương tác với người dùng sẽ tạo ra những điều không mong đợi. Tuy nhiên khi chúng
ta sử dụng $(document).ready(), thì giao diện sẽ sẵn sàng để sử dụng sớm hơn rất nhiều với
những cách xử lý mong muốn.
Lưu ý: Cách sử dụng $(document).ready() luôn được ưa chuộng hơn là sử dụng bộ quản lý
onload, nhưng chúng ta cũng nên nhớ rằng bởi vì những tệp tin hỗ trợ có thể chưa được load,
cho nên những thuộc tính như độ cao và chiều rộng của tấm hình có thể chưa có sẵn trong lúc
này. Nếu thực sự cần thiết, chúng ta có thể sử dụng bộ quản lý onload (hoặc hay hơn có thể
sử dụng jQuery để thiết lập bộ quản lý cho load event). Hai cách này hoàn toàn tương thích
với nhau.
Nhiều đoạn mã trên cùng một trang
Cách thường dùng để đăng ký bộ quản lý sự kiện thông qua JavaScript là gán một hàm cho
thuộc tính tương ứng của phần tử DOM. Giả sử như chúng ta đã định nghĩa một hàm:
function doStuff() {
//làm một cái gì đó
}
Sau đó chúng ta có thể gán nó trong phần code HTML như sau:
<body onload=”doStuff();>
Hoặc chúng ta cũng có thể gán nó trong code JavaScript:
window.onload = doStuff;
Hai cách này đều thực thi hàm khi trang được load. Nhưng điểm mạnh của cách thứ hai nằm
ở chỗ những cách xử lý được tách rời khỏi mã HTML.
Lưu ý: Bạn nên chú ý là khi chúng ta gán một hàm làm bộ quản lý, chúng ta sử dụng tên hàm
nhưng bỏ hai dấu ngoặc đơn. Nếu có hai dấu ngoặc, hàm đó sẽ được gọi ngay lập tức. Còn
nếu không có dấu ngoặc, tên hàm chỉ đơn giản được định danh, và có thể được dùng để gọi
sau này.
Nếu chỉ với một hàm thì cách này cũng sử dụng được. Nhưng nếu chúng ta có thêm một hàm
nữa:
function doOtherStuff() {
//làm một tác vụ khác
}
Sau đó chúng ta cũng thử chạy hàm này khi trang được load window.load = doOtherStuff;
Bạn sẽ thấy hàm thứ hai sẽ thắng hàm đầu tiên. Thuộc tính .onload chỉ có thể một lúc chứa
một hàm tham chiếu, cho nên chúng ta không thể thêm vào cách xử lý hiện tại. Cơ chế
$(document).ready() giải quyết trường hợp này rất êm xuôi. Mỗi một lần phương thức được
gọi, nó sẽ thêm một hàm mới vào danh sách cách xử lý nội bộ, nên khi trang được load, tất cả
các hàm sẽ được thực hiện. Các hàm sẽ thực hiện theo thứ tự mà chúng được đăng ký.
Cách viết tắt cho code ngắn gọn
Kết cấu $(document).ready() thực chất là gọi phương thức .ready() cho một đối tượng jQuery
mà chúng ta đã tạo ra từ phần tử DOM. Hàm $() cung cấp cách viết tắt cho chúng ta bởi vì nó
là một tác vụ phổ biến. Khi được gọi mà không có tham số, thì hàm này sẽ hoạt động như là
khi tài liệu đã được thông qua. Cho nên thay vì chúng ta viết:
$(document).ready(function() {
//code ở đây
});
Chúng ta có thể viết
$().ready(function() {
//code ở đây
});
Hơn nữa, hàm $() có thể lấy một hàm khác làm tham số cho nó. Cho nên khi chúng ta làm
như thế, jQuery sẽ tiến hành một lệnh gọi ẩn đến .ready(), do vậy cách viết như sau cũng cho
kết quả tương tự
$(function()
//code ở đây{
});
Tất nhiên cách viết trên ngắn gọn hơn, nhưng tôi khuyên bạn nên sử dụng kiểu viết đầy đủ để
cho rõ ràng là đoạn code này có tác dụng gì.
Cùng làm việc với những thư viện khác
Trong một vài trường hợp chúng ta cần phải sử dụng nhiều hơn một thư viện JavaScript trên
cùng một trang. Bởi vì nhiều thư viện cùng sử dụng ký hiệu nhận dạng $ do nó ngắn và thuận
tiện, cho nên chúng ta phải có cách nào đó để tránh xảy ra xung đột giữa những tên này.
Thật may mắn khi mà jQuery cung cấp một phương thức gọi là .noConflict() để trả ký hiệu
nhận dạng $ về cho các thư viện khác. Cách sử dụng phương thức .noConflict() thường thì
như sau:
<script src="prototype.js" type="text/javascript"></script>
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
jQuery.noConflict();
</script>
<script src="myscript.js" type="text/javascript"></script>
Đầu tiên thư viện Prototype được gọi, đây cũng là một thư viện JavaScript . Sau đó là bản
thân jQuery được gọi và nó sẽ sử dụng $ cho nó. Tiếp theo phương pháp .noConflict() được
gọi để giải phóng $, quyền điều khiển bây giờ lại quay trở về với thư viện được gọi đầu tiên,
ở đây là Prototype. Bây giờ code của chúng ta có thể sử dụng cả hai thư viện, nhưng bất cứ
khi nào chúng ta muốn sử dụng một phương thức jQuery, chúng ta cần phải sử dụng jQuery
thay vì dấu $ làm ký hiệu nhận dạng.
Phương thức .ready() còn có một điểm nữa có thể giúp chúng ta trong trường hợp này. Hàm
gọi ngược mà chúng ta đã chuyển cho nó có thể nhận một tham số đơn: chính là bản thân đối
tượng jQuery. Điều này cho phép chúng ta đặt lại tên cho nó mà không sợ bị xung đột.
jQuery(document).ready(function($) {
//trong đây, chúng ta có thể sử dụng $ bình thường.
});
Hoặc sử dụng kiểu viết tắt chúng ta đã học ở trên
jQuery(function($) {
//code sử dụng $
});
Sự kiện cơ bản
Có nhiều lúc chúng ta muốn thực hiện một tác vụ nào đó vào những thời điểm mà không chỉ
là lúc trang được load. Cũng như với JavaScript cho phép chúng ta đón chặn sự kiện load
trang với <body onload=”> hoặc window.onload. Nó cung cấp điểm neo cho những sự kiện
được người dùng khởi xướng như: nhấp chuột (onclick), trường nhập liệu bị thay đổi
(onchange) và cửa sổ thay đổi kích thước (onresize). Khi được gán trực tiếp vào các phần từ
trong DOM, những cách này cũng có mặt hạn chế giống như những điều chúng ta đã nói về
onload. Cho nên, jQuery cho chúng ta những cách cải tiến hơn để xử lý những sự kiện này.
Bộ nút thay đổi màu chữ
Để minh hoạ cho những cách quản lý sự kiện, giả sử chúng ta muốn có một trang web có thể
thay đổi màu sắc các đoạn văn tuỳ theo ý của người dùng. Chúng ta sẽ cho phép người dùng
nhấp chuột vào 3 nút để thay đổi màu sắc theo kiểu Mặc định, Màu đỏ và Màu Xanh. Khi
nhấn vào nút Màu Đỏ, thì nội dung sẽ chuyển thành màu đỏ, khi nhấn vào nút Màu Xanh thì
nội dung sẽ thành màu xanh và cuối cùng khi nhấn vào nút Mặc định thì nội dung quay về
trạng thái ban đầu.
Trong thực tế, người làm web có kinh nghiệm luôn áp dụng nguyên tắc nâng cao luỹ tiến.
Nếu JavaScript không được bật thì nút thay đổi màu sắc phải bị ẩn đi, còn không thì vẫn phải
hoạt động bằng các đường liên kết để cho ra những phiên bản khác nhau của trang. Trong
tutorial này, chúng ta giả sử người dùng có bật JavaScript. Dưới đây là code HTML của nút
thay đổi màu sắc của chúng ta:
<div id="switcher">
<h3>Đổi định dạng</h3>
<div class="button selected" id="switcher-default">Mặc
Định</div>
<div class="button" id="switcher-red">Màu đỏ</div>
<div class="button" id="switcher-green">Màu Xanh</div>
</div>
<div class="text">
<h1>This is the first para</h1>
<p class="chapter">Pellentesque habitant morbi tristique
senectus et netus et malesuada fames ac turpis egestas.
Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor
sit amet, ante.
Donec eu libero sit amet quam egestas semper.
</p>
<h1>This is the second para</h1>
<p>Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas.
Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor
sit amet, ante.
Donec eu libero sit amet quam egestas semper.
</p>
</div>
Để bắt đầu, chúng ta thử với nút Màu Xanh. Bạn cần phải viết một chút code CSS để chỉ cho
nó biết sẽ thay đổi như thế nào:
body.green .text{
color: green;
}
Mục đích của chúng ta sẽ là thêm một class=’green’ vào thẻ <body>. Điều này cho phép
stylesheet tái định dạng trang web sao cho phù hợp. Với kiến thức bạn học được trong
chương 2, chúng ta biết sẽ phải viết code như thế nào:
$('body').addClass('green');
Tuy nhiên, lần này chúng ta muốn khi người dùng nhấp chuột vào nút thì class=’green’ mới
được thêm vào chứ không phải như trước là khi trang được load. Để làm được việc này,
chúng ta sẽ cần đến phương thức .bind(). Phương thức này cho phép chúng ta cụ thể hoá bất
cứ một sự kiện JavaScript nào và gán một cách xử lý cho nó. Trong trường hợp này sự kiện
được gọi là click và cách xử lý là một hàm bao gồm một dòng code như ở trên:
$(document).ready(function() {
$('#switcher-green').bind('click', function() {
$('body').addClass('green');
});
});
Nếu bạn lưu lại và xem thử trên trình duyệt, khi bạn nhấp chuột vào nút màu xanh, nội dung
của nó sẽ biến thành màu xanh lá cây.
Đó là tất cả những gì bạn cần phải làm để gán một sự kiện. Thế mạnh của phương thức
.ready() cũng được sử dụng ở đây. Phương thức .bind() được gọi nhiều lần nhưng vẫn hoạt
động tốt và nhiều cách xử lý được thêm vào cùng một sự kiện khi cần thiết. Tất nhiên đây
không phải là cách hay nhất hoặc hiệu quả nhất để hoàn hành tác vụ này. Ở phần tiếp theo
của tutorial, chúng ta sẽ mở rộng và cải tiến mã của chúng ta để chúng ta có thể tự hào về nó.
Làm các nút khác hoạt động
Bây giờ chúng ta đã có nút màu xanh hoạt động như ý, việc tiếp theo chúng ta phải làm là
cho hai nút còn lại là Mặc định và Màu Đỏ cũng có thể hoạt động được. Vấn đề khá là đơn
giản, chúng ta sẽ sử dụng phương thức .bind() để thêm vào một bộ xử lý click cho mỗi một
nút, thêm hoặc bỏ class khi cần. Chúng ta sẽ viết mã như sau:
$(document).ready(function() {
$('#switcher-default').bind('click', function() {
$('body').removeClass('red').removeClass('green');
});
$('#switcher-red').bind('click', function(){
$('body').addClass('red').removeClass('green');
});
$('#switcher-green').bind('click', function() {
$('body').addClass('green').removeClass('red');
});
});
Trong file stylesheet bạn phải có luật sau
body.red .text {
color: red;
}
Với đoạn mã ở trên chúng ta đã tiến hành những việc như sau:
1. 1.Nếu người dùng nhấn vào thẻ div với ID #switcher-default thì sẽ bỏ cả hai class là
red và green đi.
2. 2.Nếu người dùng nhấn vào thẻ div với ID #switcher-red thì chúng ta sẽ thêm
class=’red’ và bỏ class=’green’ đi.
3. 3.Tương tự như bước 2 nhưng bỏ class=’red’ và thêm class=’green’.
Và bây giờ khi nhấn vào nút Màu Đỏ thì chữ sẽ được tô thành màu đỏ
Bộ xử lý sự kiện ngữ cảnh
Nút thay đổi màu sắc của chúng ta đã làm việc như mong muốn, nhưng người dùng không
biết được là nút nào đang bị bấm. Cách chúng ta sẽ làm là thêm class=’selected’ cho nút nào
đang được chọn và bỏ class đó đi ở những nút không được nhấp. Chúng ta chỉ đơn giản làm
cho nút đang được chọn được tô đậm hơn một chút.
.selected {
font-weight: bold;
}
Chúng ta cũng có thể làm tương tự như cách đã làm ở trên bằng cách gọi mỗi nút bằng ID
riêng và thêm và bỏ class nếu cần. Nhưng thay vào đó, chúng ta có thể tìm hiểu thêm một giải
pháp khác hay hơn và linh động hơn. Nó sẽ sử dụng ngữ cảnh mà bộ xử lý sự kiện đang hoạt
động.
Bất cứ khi nào một bộ xử lý sự kiện được kích hoạt, thì từ khoá this đại diện cho phần tử
DOM được gán một kiểu xử lý. Ở những phần trước bạn cũng đã biết rằng hàm $() có thể lấy
một phần tử DOM làm tham số cho nó. Bằng cách viết $(this) trong bộ xử lý sự kiện, chúng
ta tạo ra một đối tượng jQuery tương ứng với phần tử DOM, và chúng ta có thể thao tác với
nó như là chúng ta đã chọn nó bằng bộ chọn CSS. Do đó chúng ta có thể viết
$(this).addClass('selected');
Chèn dòng code này vào cả 3 bộ xử lý nó sẽ thêm class=’selected’ mỗi khi nút được nhấn. Để
loại bỏ class ở những nút khác, chúng ta có thể tận dụng chức năng vòng lặp ẩn của jQuery
để có:
$('#switcher .button').removeClass('selected');
Khi bạn chèn dòng code trên vào code jQuery của bạn, nó sẽ loại bỏ hết các class ở tất cả các
thẻ div có class=’button’.
$(document).ready(function() {
$('#switcher-default').bind('click', function() {
$('body').removeClass('red').removeClass('green');
$('#switcher .button').removeClass('selected');
$(this).addClass('selected')
});
$('#switcher-red').bind('click', function(){
$('body').addClass('red').removeClass('green');
$('#switcher .button').removeClass('selected');
$(this).addClass('selected');
});
$('#switcher-green').bind('click', function() {
$('body').addClass('green').removeClass('red');
$('#switcher .button').removeClass('selected');
$(this).addClass('selected')
});
});
Khái quát chung các mệnh đề bằng cách sử dụng bộ xử lý ngữ cảnh cho phép chúng ta làm
việc hiệu quả hơn. Chúng ta có thể tách phần highlight nút được chọn vào một bộ xử lý riêng,
bởi vì nó giống nhau ở cả 3 nút. Cho nên chúng ta có thể làm như sau:
$(document).ready(function() {
$('#switcher-default').bind('click', function() {
$('body').removeClass('red').removeClass('green');
});
$('#switcher-red').bind('click', function(){
$('body').addClass('red').removeClass('green');
});
$('#switcher-green').bind('click', function() {
$('body').addClass('green').removeClass('red');
});
$('#switcher .button').bind('click', function() {
$('#switcher .button').removeClass('selected');
$(this).addClass('selected');
});
});
Đoạn tối ưu hoá mã này sử dụng 3 chức năng của jQuery mà chúng ta đã học. Đầu tiên là
vòng lặp ẩn được sử dụng để gắn cùng một bộ xử lý click cho mỗi nút bấm với một lần gọi
.bind(). Thứ hai, thứ tự cách xử lý cho phép chúng ta gán hai hàm cho cùng một sự kiện click
mà hàm thứ 2 không đè lên hàm thứ nhất. Cuối cùng, chúng ta sử dụng khả năng kết hợp của
jQuery để ghép hai đoạn thêm và bỏ class trên cùng một dòng.
Tiếp tục tối giản mã
Công đoạn tối ưu hoá mã chúng ta vừa làm ở trên là một ví dụ của tái cơ cấu. Tái cơ cấu mã
có nghĩa là chúng ta phải chỉnh sửa mã đang có sao cho nó có thể hoạt động hiệu quả hơn và
gọn gàng hơn. Để tìm ra thêm những yếu tố khác có thể tái cơ cấu mã, chúng ta hãy xem xét
những cách xử lý mà chúng ta vừa gắn cho mỗi nút. Tham số của phương thức
.removeClass() là không bắt buộc, khi được bỏ qua, nó loại bỏ tất cả các class của phần tử.
Dựa vào điều này, chúng ta có thể rút ngắn mã xuống chút xíu nữa.
$(document).ready(function() {
$('#switcher-default').bind('click', function() {
$('body').removeClass();
});
$('#switcher-red').bind('click', function(){
$('body').removeClass().addClass('red');
});
$('#switcher-green').bind('click', function() {
$('body').removeClass().addClass('green');
});
$('#switcher .button').bind('click', function() {
$('#switcher .button').removeClass('selected');
$(this).addClass('selected');
});
});
Ở đoạn mã trên thứ tự của các tác vụ bị thay đổi một chút để phù hợp với việc loại bỏ class
không sử dụng tham số. Chúng ta cần phải gọi phương thức .removeClass() trước, như thế nó
sẽ không loại bỏ mất class khi chúng ta gọi .addClass() trên cùng một dòng.
Lưu ý: Trong ví dụ này chúng ta có toàn quyền quyết định với mã HTML, cho nên chúng ta
có thể loại bỏ toàn bộ class. Tuy nhiên khi chúng ta viết mã để sử dụng lại (như là viết một
Plugin), thì chúng ta nên tôn trọng những class khác và giữ nguyên chúng.
Hiện tại chúng ta vẫn chạy cùng một đoạn mã cho mỗi bộ xử lý nút bấm. Phần này có thể tái
cơ cấu rất dễ dàng bởi một bộ xử lý nút chung:
$(document).ready(function() {
$('#switcher .button').bind('click', function() {
$('body').removeClass();
$('#switcher .button').removeClass('selected');
$(this).addClass('selected');
});
$('#switcher-red').bind('click', function(){
$('body').removeClass().addClass('red');
});
$('#switcher-green').bind('click', function() {
$('body').removeClass().addClass('green');
});
});
Chúng ta đã di chuyển bộ xử lý nút chung lên trên những nút khác. Vậy nên phương thức
.removeClass() cần phải xảy ra trước khi phương thức .addClass() được gọi. Việc này có thể
làm được bởi vì jQuery luôn kích hoạt bộ xử lý sự kiện theo thứ tự mà chúng được đăng ký.
Cuối cùng chúng ta cũng có thể loại bỏ luôn cả các bộ xử lý cho từng nút bằng cách sử dụng
sự kiên ngữ cảnh. Bởi vì từ khoá this cho chúng ta một phần tử DOM chứ không phải là một
một đối tượng jQuery, chúng ta có thể sư dụng những tính năng của DOM để xác định ID của
mỗi phần tử khi nó được click. Do đó chúng ta có thể gán cùng một bộ xử lý cho tất cả các
nút, và những bộ xử lý này tiến hành những tác vụ khác nhau cho mỗi nút bấm.
$(document).ready(function() {
$('#switcher .button').bind('click', function() {
$('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');
});
});
Trên đây chúng ta chỉ sử dụng một mệnh đề if … else bình thường để kiểm tra xem nút đang
được click có ID là gì rồi đưa ra cách xử lý tương ứng cho từng trường hợp.
Những sự kiện viết tắt
Gắn một bộ xử lý cho một sự kiện (ví dụ như là một sự kiên click) rất thường xảy ra, cho nên
jQuery cung cấp một cách ngắn gọn hơn. Đó chính là những phương pháp viết tắt sự kiện, nó
hoạt động giống như khi chúng ta sử dụng .bind() nhưng tiết kiệm được vài chữ.
Ví dụ, đoạn mã của chúng ta có thể sử dụng .click() thay vì .bind() như sau:
$(document).ready(function() {
$('#switcher .button').click(function() {
$('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');
});
});
Những phương pháp viết tắt sự kiện như thế này tồn tại với tất cả những sự kiện DOM tiêu
chuẩn:
· blur
· change
· click
· dblclick
· error
· keydown
· keypress
· keyup
· load
· mousedown
· mousemove
· mouseout
· mouseover
· mouseup resize
· scroll
· select
· submit
· unload
Mỗi phương pháp viết tắt sẽ gán một bộ xử lý vào một sự kiện với tên tương ứng
Sự kiện phức hợp
Mỗi một phương thức xử lý sự kiện của jQuery đều tương ứng trực tiếp với một sự kiện
thuần JavaScript. Tuy nhiên, jQuery có một vài bộ xử lý riêng được thêm vào cho dễ sử dụng
và tối ưu hoá việc tương thích các trình duyệt. Một trong những phương thức này chính là
.ready(), mà chúng ta đã bàn ở trên. Hai phương thức .toggle() và .hover() là hai bộ xử lý sự
kiện tuỳ chỉnh nữa của jQuery. Chúng đều được gọi là bộ xử lý sự kiện phức hợp bởi vì
chúng tương tác với người dùng và phản ứng lại với họ sử dụng nhiều hơn một hàm.
Ẩn và hiện những tính năng tiên tiến
Giả sử chúng ta muốn ẩn bộ thay đổi màu sắc khi không cần thiết bằng cách làm cho ba nút
biến mất. Chúng ta sẽ cho phép người dùng nhấp chuột vào phần tiêu đề “đổi kiểu dáng” để
ẩn nút ấn đi nhưng vẫn giữ nguyên tiêu đề. Nếu người dùng nhấp chuột thêm lần nữa sẽ hiện
lại các nút bấm. Chúng ta cần thêm một class nữa để xử lý những nút được ẩn.
.hidden {
display: none;
}
Chúng ta có thể thêm chức năng vừa nói ở trên vào bằng cách lưu trạng thái hiện tại của nút
vào một biến, sau đó kiểm tra giá trị của nó mỗi khi tiêu đề được nhấp để xác định nên hay
không nên loại bỏ class=’hidden’ trên các nút bấm. Chúng ta cũng có thể kiểm tra trực tiếp sự
hiện diện của class trên một nút, và sử dụng thông tin này để quyết định sẽ phải làm gì.
Nhưng thay vào đó, jQuery cho chúng ta một phương thức gọi là .toggle(), sẽ làm tất cả
những việc phức tạp trên cho mình.
Phương thức .toggle() có thể lấy hai hoặc nhiều tham số, mỗi một tham số là một hàm. Khi
nhấp chuột lần đầu sẽ chạy hàm thứ nhất, nhấp chuột lần thứ hai sẽ kích hoạt hàm thứ hai
v.v.. Khi mỗi hàm đã được kích hoạt, vòng lặp lại bắt đầu từ hàm nhứ nhất. Với .toggle(),
chúng ta có thể tiến hành ẩn hiện nút thay đổi kiểu dáng khá đơn giản:
$(document).ready(function() {
$('#switcher h3').toggle(function() {
$('#switcher .button').addClass('hidden');
$('#switcher .button').removeClass('hidden');
});
$('#switcher .button').click(function() {
$('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');
});
});
Sau khi nhấp chuột một lần nút sẽ bị ẩn đi
Nhấp chuột thêm lần nữa sẽ hiện lại
Một lần nữa chúng ta lại sử dụng vòng lặp ẩn để ẩn các nút đi mà không cần phải sử dụng
đến một phần tử bao quanh. Với trường hợp cụ thể này, jQuery cung cấp một cách nữa dùng
để ẩn hiện bộ nút của chúng ta. Chúng ta có thể sử dụng phương thức .toggleClass() để tự
động kiểm tra sự hiện diện của class trước khi thêm hoặc loại bỏ nó
$(document).ready(function() {
$('#switcher h3').click(function() {
$('#switcher .button').toggleClass('hidden')
});
Trong trường hợp này, .toggleClass() là giải pháp hay hơn, nhưng .toggle() là cách linh hoạt
hơn khi tiến hành hai hoặc nhiều tác vụ khác nhau.
Highlight nút bấm
Để chứng minh cho khả năng của sự kiện click có thể làm việc với những thành phần không
nhấp chuột được, chúng ta đã tạo ra một giao diện với nhứng nút mà thực chất chỉ là những
thẻ div trở thành một phần sống động của trang, trực chờ người dùng sử dụng nó. Bây giờ
chúng ta có thể làm cho nút bấm có thể thay đổi trạng thái khi di chuột qua, cho người dùng
biết rằng những nút này sẽ làm một việc gì đó nếu được bấm.
#switcher .hover {
cursor: pointer
background-color: #afa;
}
CSS cung cấp một pseudo-class gọi là :hover, cho phép styesheet chi phối một thành phần
khi người dùng di chuột qua nó. Nhưng trong IE6, chức năng này bị giới hạn chỉ với những
đường liên kết, cho nên chúng ta không sử dụng nó cho tất cả các trình duyệt được. Thay vào
đó, jQuery cho phép chúng ta sử dụng JavaScript để thay đổi kiểu dáng của một phần tử và
có thể tiến hành bất cứ tác vụ nào lên nó. Kể cả khi di chuột lên phần tử và di chuột ra khỏi
phần tử đó.
Phương thức .hover() lấy hai tham số, giống như ví dụ về .toggle() ở trên. Trong trường hợp
này, hàm đầu tiên sẽ được thực hiện khi chuột di qua nó và hàm thứ hai sẽ được kích hoạt khi
chuột ra khỏi nó. Chúng ta có thể thay đổi class cho các nút tại thời điểm này để tạo ra hiệu
ứng rollover:
$(document).ready(function() {
$('#switcher h3').click(function() {
$('#switcher .button').toggleClass('hidden')
});
$('#switcher .button').hover(function() {
$(this).addClass('hover');
}, function() {
$(this).removeClass('hover');
});
Chúng ta lại một lần nữa sử dụng vòng lặp ẩn và ngữ cảnh sự kiện để có đoạn mã ngắn hơn
và đơn giản hơn. Nếu bây giờ khi bạn di chuột qua các nút bấm, bạn sẽ thấy được hiệu ứng
Rollover như hình
Sử dụng .hover() cũng giúp chúng ta tránh được những rắc rối tạo ra bởi lan truyền sự kiện
(event propagation) trong JavaScript. Để hiểu được lan truyền sự kiện là gì, chúng ta hãy
xem xét JavaScript quyết định phần tử nào sẽ được xử lý kiện.
Đường đi của một sự kiện
Khi một sự kiện xảy ra trên một trang, toàn bộ cấu trúc bậc thang của các phần tử DOM đều
có cơ hội để xử lý sự kiện. Thử tưởng tượng một mô hình như sau:
<div class='mainContent'>
<span class='date'><a href='httt://www.izwebz.com'>jQuery tutorial from
izwebz</a></span>
<p> Khi một sự kiện xảy ra trên một trang, toàn bộ cấu trúc bậc thang của
các phần tử DOM đều có cơ hội để xử lý sự kiện.
</p>
Chúng ta có thể hình tượng hoá đoạn code trên với hình minh họa sau
Với mỗi một sự kiện, sẽ có nhiều phần tử có thể chịu trách nhiệm xử lý. Ví dụ khi đường link
ở ví dụ trên được bấm thì ba thành phần như <div>, <span> và <a> đều có cơ hội để phản
ứng lại click đó. Bởi vì bạn thấy cả 3 thành phần trên đều nằm dưới con trỏ chuột của người
dùng. Nhưng phần tử <p> lại không nằm trong mối tương tác này.
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.
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 mouseenter và mouseleave 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() và .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() và .die(). Hai
phương pháp này làm việc giống nhứ .bind() và .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ỏ.