jQuery 事件處理 (Events)
jQuery 的事件處理函式大都提供兩種用途,一種是呼叫帶有參數的函式 - 綁定事件處理函式;另一種則是呼叫不帶有參數的函式 - 觸發該事件。
帶有參數,例如,綁定所有段落觸發 click 事件時,將背景顏色改為藍色:
$('p').click(function() {
$(this).css('background-color', 'blue');
});
不帶有參數,例如,觸發所有段落的 click 事件:
$('p').click();
this
為被觸發的「DOM元素」,而非 jQuery 物件。上述的程式碼,我們用到 jQuery 定義好的 click 函式來處理 click event,然而 jQuery 也同樣對 DOM 其它的 event 都有相關的函式,如以下的 jQuery 事件函式也是同樣的使用方法:
事件 | 觸發條件 |
---|---|
blur | 物件失去焦點時 |
change | 物件內容改變時 |
click | 滑鼠點擊物件時 |
dblclick | 滑鼠連點二下物件時 |
error | 當圖片或文件下載產生錯誤時 |
focus | 當物件被點擊或取得焦點時 |
keydown | 按下鍵盤按鍵時 |
keypress | 按下並放開鍵盤按鍵後 |
keyup | 按下並放開鍵盤按鍵時 |
load | 網頁或圖片完成下載時 |
mousedown | 按下滑鼠按鍵時 |
mousemove | 介於over跟out間的滑鼠移動行為 |
mouseout | 滑鼠離開某物件四周時 |
mouseover | 滑鼠離開某物件四周時 |
mouseup | 放開滑鼠按鍵時 |
resize | 當視窗或框架大小被改變時 |
scroll | 當捲軸被拉動時 |
select | 當文字被選取時 |
submit | 當按下送出按紐時 |
beforeunload | 當使用者關閉 (或離開) 網頁之前 |
unload | 當使用者關閉 (或離開) 網頁之後 |
jQuery 的 event object
對於所有的 jQuery event handler,你都能傳入一個參數作為 event 物件,如下例:
$(document).click(function(event) {
alert(event.pageX);
});
上面這個例子是用來取得滑鼠游標相對於頁面的位置,重點是!這段程式碼在IE上也能執行 (註:原本的 IE 瀏覽器事件無 pageX 屬性)。Why? 因為 jQuery 又事先幫你解決掉事件處理中跨瀏覽器的問題了 - 「jQuery 修改了 event object 以使其符合 W3C 的標準」,所以從此你不用再重複 if (window.event)
,你只需了解並使用標準的事件屬性!
jQuery 的事件綁定處理函式
.hover(handlerIn, handlerOut)
$(selector).hover(handlerIn, handlerOut)
其實是 $(selector).mouseenter(handlerIn).mouseleave(handlerOut);
的簡化版。
當滑鼠移動到一個匹配的元素上面時,會觸發第一個函數 (handlerIn);當滑鼠移出該元素時,會觸發第二個函數 (handlerOut)。
例如,滑鼠移進 li 元素時,在後面加上 ***,移出時再刪掉:
$('li').hover(
function() {
$(this).append($('<span> ***</span>'));
}, function() {
$(this).find('span:last').remove();
}
);
.bind(eventType, handler)
unbind(eventType)
除了直接用特定的事件函式來綁定事件 (i.e. .click()),你也可以用 .bind()
來做。
例如,滑鼠點 foo 元素時跳出 alert 訊息:
$('#foo').bind('click', function() {
alert('User clicked on foo.');
});
.unbind()
用來移除事件處理函式,例如:
// 移除 foo 元素上所有綁定的事件處理函式
$('#foo').unbind();
// 只移除 foo 元素上所有綁定的 click 事件處理函式
$('#foo').unbind('click');
// 只移除特定事件的特定處理函式
var handler = function() { alert('hi'); };
$('#foo').bind('click', handler);
$('#foo').unbind('click', handler);
bind 及 unbind 還可以讓我們達到 自訂事件處理 的功能:
$('#foo').bind('myEvent', doSomething);
如上,我們自訂一個叫作 myEvent 的事件,但這不是 DOM 標準事件啊,怎麼觸發它?答案是用接著會談到的 "trigger" 函式來觸發 myEvent。
.trigger(eventType [, extraParameters])
觸發事件,其中 extraParameters 為要傳給事件處理函式的參數 (一個陣列或物件)。
例如,觸發上面的自訂 myEvent 事件:
$('#foo').trigger('myEvent');
當然也可以用來觸發一般事件:
$('#foo').trigger('click');
.one(events, handler)
如果只是觸發 "一次" 事件,就使用 one 函式來作 bind 的動作,當該事件被觸發一次之後就會自己自動 unbind。
$('#foo').one('click', function() {
alert('This will be displayed only once.');
});
.on()
.off()
從 jQuery 1.7 開始 .on()
是主要推薦使用的事件處理綁定函式,也用來取代 .bind()
, .live()
和 .delegate()
。
.bind()
的缺點在於 bind 是直接將事件處理函式直接綁定在選取的元素上面,所以在綁定當時,元素必須是已經存在 DOM 中的!
例如,這樣子是沒用的:
// 這時頁面上還沒有 #foo 這元素
$('#foo').bind('click', function() { alert('hi'); });
$(document.body).append('<button id="foo">hi</button>');
// 點 button 不會有 alert!
.on() 充分利用瀏覽器事件傳播 (event bubbling) 的原理和事件委任 (event delegation) 的技巧!
因為事件委任的技巧讓 .on() 可以用來綁定事件處理在現在已經存在或還沒存在的 DOM 元素,像是你可以綁在 $(document).on() 監聽 DOM 的所有事件。
利用 event delegation 還有一個效能上的優點,你不用在一堆元素上各別綁定事件處理 (例如綁上千個元素,效能差),你可以只綁在 document 或 container element 上面,獲得顯著的效能改善。
例如,綁一堆 click 事件處理在 #dataTable tbody 下面的一堆 tr 元素上:
$('#dataTable tbody tr').on('click', function() {
// 這裡的 this 指向 #dataTable tbody tr 這一個 DOM 元素
// 用 $() 將 this 轉成 jQuery object
console.log( $(this).text() );
});
V.S. 綁一個 click 事件處理在 #dataTable tbody 上,然後監聽從 tr bubble 上來的 click 事件:
$('#dataTable tbody').on('click', 'tr', function() {
// 這裡的 this 指向符合 selector 的 DOM 元素,也就是 tr
console.log( $(this).text() );
});
.on() 的第三個參數,可以用來傳資料進事件處理函式:
function greet(event) {
alert('Hello ' + event.data.name);
}
$('button').on('click', {
name: 'Karl'
}, greet);
$('button').on('click', {
name: 'Addy'
}, greet);
用 .off() 來移除事件處理函式,例如:
// 移除所有 p 元素的事件處理
$('p').off();
// 移除所有 p 元素的 click 事件處理
$('p').off('click');
// 移除 #foo 的 click 事件委任
$('p').off('click', '#foo');
還可以用空白隔開多個事件,同時綁定事件處理:
$('#cart').on('mouseenter mouseleave', function(event) {
$(this).toggleClass('active');
});
替事件命名 (Namespacing events)
我們雖然可以用 unbind / off 來移除事件,但是它會一次移除全部綁定的事件處理耶,何解?答案是利用 jQuery 提供的 Namespacing events!看個例子就明白它是什麼:
// 綁定事件處理函式;並幫這事件命名為 name
// 事件名稱要接在句點 . 後面
$('#foo').on('click.name', function() { alert(1); });
$('#foo').on('click.name', function() { alert(2); });
// 觸發所有名稱是 name 的事件
$('#foo').trigger('.name');
// 移除所有名稱是 name 的事件
$('#foo').off('.name');
Namespacing events 簡單的說就是幫事件取個名字 (namespace),了解吧 :)
$(document).ready(function() {})
最後,但是很重要也很常用的。jQuery 中,大部分的操作都基於 HTML DOM,所以我們必須確定頁面文件已經完全下載好才開始執行你的程式,jQuery 提供下面這個函式來處理 DOM ready 事件 (DOMContentLoaded):
$(document).ready(function() {
// 這裡放你要執行的程式碼
});
你也可以這樣寫:
$(function() {
// 這裡放你要執行的程式碼
});
DOMContentLoaded & 外部樣式表的問題?
如上述,DOMContentLoaded 事件的觸發不必等到圖片檔案等下載完畢,但這裡有個問題就是,那需要等到 "外部樣式表 (External CSS Styles)" 下載完畢嗎?關於這一點,各家瀏覽器對於 DOMContentLoaded 事件的觸發也的確不一致,在 Opera 中瀏覽器不會等外部樣式表下載完即觸發 DOM ready;而 Firefox 則會等到外部 CSS 都加載完畢。因此若你有在 jQuery ready 事件中改變了元素的 CSS 屬性,對於 Opera,你修改的屬性很可能會被接著加載的外部樣式表覆蓋過去,這也就造成跨瀏覽器處理的問題。但如果你無法避免在頁面初始階段改變 CSS 屬性,這問題我們可以利用 load 事件來避開,以確定事件的觸發會在外部樣式表下載完畢之後:
$(window).load(function() {
// Run code
});