1.事件冒泡
在前端網頁程式設計中元素動作的事件處理有一個特別的設計,就是事件會像泡泡一樣,由引發事件的起源點向網頁的根節點一層一層的向上傳遞,這個動作稱為事件冒泡(event bubbling)。
假設有一網頁HTML原始碼如下:
<!DOCTYPE html>
<html>
<body>
<div id="content" style="width:600px; background-color: #CCEEFF; padding: 20px">
<span>content box</span>
<div id="handler" style="background-color: #99FF99; padding: 20px">
<span>handler box</span>
<table id="datatable" style="width:100%; background-color: #FFFFBB;">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr data-id="A001">
<td>A001</td>
<td>Mary</td>
<td><input type="button" value="click"></td>
</tr>
<tr data-id="A002">
<td>A002</td>
<td>Jone</td>
<td><input type="button" value="click"></td>
</tr>
<tr data-id="A003">
<td>A003/td>
<td>LiLi</td>
<td><input type="button" value="click"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="messagewall" style="width:600px; border: 1px solid; background-color: #f0f0f0; margin-top: 20px">
</div>
</body>
</html>
網頁執行結果顯示如下:
頁面上共有二個div區塊容器,分別為content及handler,及一個table datatable,其階層關係為content > handler > datatable。
接著為頁面上的元素綁定事件處理函式如下,當元素發生click事件時,在div messagewall內容顯示相對應文字。
<script>
$(function(){
$(window).on('click',
function (e) {
addmsg('> window click.');
});
$(document).on('click',
function (e) {
addmsg('=> document click.');
});
$('body').on('click',
function (e) {
addmsg('==> body click.');
});
$('#content').on('click',
function (e) {
addmsg('===> content box click.');
});
$('#handler').on('click',
function (e) {
addmsg('====> handler box click.');
});
$('#datatable').on('click',
function (e) {
addmsg('=====> datatable click.');
});
$('#datatable tr').on('click',
function (e) {
addmsg('======> datatable tr click.');
});
$('#datatable td').on('click',
function (e) {
addmsg('=======> datatable td click.');
});
$('#datatable tr input[type="button"]').on('click',
function (e) {
addmsg('========> button click.');
});
});
function addmsg(msg) {
$('<div>').html(msg).appendTo('#messagewall');
}
</script>
當我們點繫頁面表格第一列A001的click button時,會得到結果如下,顯示並不是只有button觸發click單一事件執行完成後就結束,而是會將被觸發的事件event object逐層的往上傳送,經過table td、tr、table…各個節點並嘗試觸發該節點的相同事件,若有綁定相同事件則執行相對應的事件處理程式,若沒有則繼續傳送,直到網頁DOM最上層根節點為止,有如水中泡泡的上浮一般,稱之為事件冒泡。
========> button click.
=======> datatable td click.
======> datatable tr click.
=====> datatable click.
====> handler box click.
===> content box click.
==> body click.
=> document click.
> window click
將網頁內容轉為DOM樹狀結構及橫向剖面,及事件傳遞表示如下:
2.事件委派
那我們可以利用事件氣泡的特性做什麼事呢? 再來看一個範例,將table最後欄位修改顯示二個button為ok及cancel,程式碼及呈現畫面如下:
<td>
<input type="button" value="ok">
<input type="button" value="cancel">
</td>
接下來修改事件綁定只在handler div加上click事件處理程式如下,當補捉到click事件時首先取得原始觸發事件的元素(event.target),並判斷元素的值若為ok則呼叫執行ok()函式,若值為cancel則呼叫執行cancel()函式。
$(function(){
$('#handler').on('click',
function (event) {
let target = event.target;
if(target.value == 'ok') {
ok(target);
}
else if(target.value == 'cancel') {
cancel(target);
}
});
});
function ok(target) {
addmsg('=> ' + $(target).closest('tr').data('id') + ' ok button click.');
}
function cancel(target) {
addmsg('=> ' + $(target).closest('tr').data('id') + ' cancel button click.');
}
此時若分別點擊了表格第一列A001的ok button、第二列A002的cancel button、第三列A003的ok button,則結果顯示文字如下:
=> A001 ok button click.
=> A002 cancel button click.
=> A003 ok button click.
運用這種模式,我們可以很容易的統一固定在某一個層級(元素)上,監聽捕捉我們有興趣的事件並處理,好處是有一致的事件處理程序易於維護,且不需要刻意逐一的去維護內部元素的事件處理綁定,避免遺漏的發生,也減輕瀏覽器的負擔,增加執行效率,例如表格內各列的按鈕,當表格資料刷新時必須重新綁定一次,當在前端操作新增加入一筆資料時,又必須記得額外再做一次綁定,複雜程式碼及重工,且增加發生問題的機率。
3.自訂事件
前面範例是補捉原生的事件並判斷觸發事件的來源做不同的處理,如果要使用不同的自訂事件來區分應該怎麼做呢? 我們可以這麼做,首先將table各列的button增加onclick屬性如下,當觸發click事件時,觸發指定的ok或cancel自訂事件。
<input type="button" value="ok" onclick="$(this).trigger('ok');">
<input type="button" value="cancel" onclick="$(this).trigger('cancel');">
接著修改事件綁定只在handler div加上ok及cancel事件處理程式如下:
$(function(){
$('#handler').on('ok',
function (event) {
addmsg('=> ' + $(event.target).closest('tr').data('id') + ' ok button click.');
});
$('#handler').on('cancel',
function (event) {
addmsg('=> ' + $(event.target).closest('tr').data('id') + ' cancel button click.');
});
});
此時若分別點擊了表格第一列A001的cancel button、第二列A002的ok button、第三列A003的cancel button,則結果顯示文字如下,同樣達到目的。
=> A001 cancel button click.
=> A002 ok button click.
=> A003 cancel button click.
4.終止事件氣泡傳播
有時候因為網頁頁面上有較複雜的操作或不同層次中有相同的事件處理,我們希望事件能夠在被觸發的元素上執行完成後即中止,不按照預設的行為向上傳播發散,減少瀏覽器處理執行時間或避免重複誤觸發其他事件執行,可以怎麼做呢? 在Javascript提供以下三個方式語法可運用:
(1) event.preventDefault()
當執行event.preventDefault()後會對元素事件終止預設行為(Stop Event Flow),例如如下範例,使用一個a hyper link元素,並綁定其click事件,在事件我們可以撰寫事件卻處理的程序邏輯及event.preventDefault()語法,當觸發click事件時hyper link就不會執行連結導頁的動作了,因為己經要求事件停止預設行為。
<a href="#">click me</a>
<script type="text/javascript">
$("a").click(function(event) {
event.preventDefault();
// do something…
});
</script>
(2) event.stopPropagation()
當執行event.stopPropagation()後會終止事件傳播,例如如下範例,在a hyper link元素外層加上div容器,並個別綁定click事件,在a hyper link事件程序中加了event.stopPropagation()語法,則當hyper link被點取時只會執行a hyper link的事件,不會向上傳導觸發div click事件,但須特別注意的是a hyper link在事件處理後仍會執行預設連結導頁動作。
<div>
<a href="#">click me</a>
</div>
<script type="text/javascript">
$("div").click(function() {
alert("div click!");
});
$("a").click(function(event) {
event.stopPropagation();
// do something…
});
</script>
(3) return false
當在事件處理程序中執行了return false,其實包含了以下三種行為:
• event.preventDefault()
• event.stopPropagation()
• 停止程序執行並回傳
如下範例,在a hyper link元素外層加上div容器,並個別綁定click事件,在a hyper link事件程序中加了return false,則當hyper link被點取時只會執行a hyper link事件顯示”link click 1!”提示訊息,不會顯示”link click 2!”提示訊息,並且取消連結導頁動作,亦不會向上傳導觸發div click事件。
<div>
<a href="#">click me</a>
</div>
<script type="text/javascript">
$("div").click(function() {
alert("div click!");
});
$("a").click(function() {
alert("link click 1!");
return false;
alert("link click 2!");
});
</script>
5.Event Object
在HTML DOM的規範裡,事件都是以物件的型式存在,最後我們來看一下,當一個事件發生時可以從事件物件取得那些常用的資訊,詳細可參考w3schools。
Event Object
屬性 |
說明 |
currentTarget |
取得目前觸發事件的元素參考。 |
target |
取得觸發事件的原始元素參考。 |
timeStamp |
取得事件建立的日期時間。 |
type |
取得事件的名稱,例如click。 |
MouseEvent Object
屬性 |
說明 |
altKey |
取得滑鼠事件觸發時是否有按下ALT鍵。 |
button |
取得滑鼠事件觸發時哪一個滑鼠鍵被按下。 |
buttons |
取得滑鼠事件觸發時哪些滑鼠鍵被按下。 |
clientX |
取得滑鼠事件觸發時滑鼠指標對應目前視窗的水平座標位置點。 |
clientY |
取得滑鼠事件觸發時滑鼠指標對應目前視窗的垂直座標位置點。 |
ctrlKey |
取得滑鼠事件觸發時是否有按下CTRL鍵。 |
pageX |
取得滑鼠事件觸發時滑鼠指標對應文本的水平座標位置點。 |
pageY |
取得滑鼠事件觸發時滑鼠指標對應文本的垂直座標位置點。 |
relatedTarget |
取得觸發滑鼠事件的元素 |
screenX |
取得滑鼠事件觸發時滑鼠指標對應螢幕的水平座標位置點。 |
screenY |
取得滑鼠事件觸發時滑鼠指標對應螢幕的垂直座標位置點。 |
shiftKey |
取得滑鼠事件觸發時是否有按下SHIFT鍵。 |
KeyboardEvent Object
屬性 |
說明 |
altKey |
取得鍵盤事件觸發時是否有按下ALT鍵。 |
ctrlKey |
取得鍵盤事件觸發時是否有按下CTRL鍵。 |
charCode |
取得鍵盤事件觸發時onkeypress事件按下的Unicode字元碼。 |
key |
取得鍵盤事件觸發時按下的按鍵碼。 |
keyCode |
取得鍵盤事件觸發時onkeypress或onkeydown或onkeyup事件的Unicode字元碼。 |
shiftKey |
取得鍵盤事件觸發時是否有按下SHIFT鍵。 |