js事件冒泡
js所謂的事件冒泡就是子級元素的某個事件被觸發,它的上級元素的該事件也被遞歸執行
html:
<ul class="clearfix" data-type="cityPick"> <li class="active_sort_opts" data-id="0">全部</li> <li data-id="88">紐約</li> <li data-id="119">洛杉磯</li> <li data-id="138">拉斯維加斯</li> <li data-id="84">夏威夷</li> <li data-id="120">舊金山</li> <li data-id="105">奧蘭多</li> <li data-id="118">西雅圖</li> </ul>
js:
$("ul[data-type='cityPick']").on('click',function(){ alert("父元素ul被點擊"); }); $("ul[data-type='cityPick']").on('click','li',function(){ alert("子元素li被點擊"); });
當li的點擊事件被觸發時,父級ul的點擊事件也被觸發執行了,
而在某些場合我們是不希望它冒泡的,怎么做呢? 簡單!
js:
$("ul[data-type='cityPick']").on('click',function(){ alert("父元素ul被點擊"); }); $("ul[data-type='cityPick']").on('click','li',function(e){ e.stopPropagation();//阻止冒泡 alert("子元素li被點擊"); });
加上e.stopPropagation(); 這一句便可以阻止事件冒泡了
js事件委托
js事件委托,其實是使用了冒泡的原理,從點擊的元素開始,遞歸方式的向父元素傳播事件,這樣做的好處是對於大量要處理的元素,不必為每個元素都綁定事件,只需要在他們的父元素上綁定一次即可,提高性能。 還有一個好處就是可以處理動態插入dom中的元素,直接綁定的方式是不行的。
就是事件目標自身不處理事件,而是把處理任務委托給其父元素或者祖先元素,甚至根元素事件委托很好地利用了"事件冒泡"。當點擊子元素,根據"事件冒泡",該子元素的父級元素捕獲了該次點擊事件,並觸發自己的方法。
看實例:
假如現在有10個按鈕,要為每個按鈕綁定一個click事件,可能才十個按鈕,你可以一個一個的綁定或用循環進行綁定,但是這樣性能呢?
html:
<div class="button-group"> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> <bottoun type="button" class="btn">提交</bottoun> </div>
js:
$(".button-group").on('click','.btn',function(){ alert($(this).html()); });
這里可以看出,我們只是為所有的button的一個父級綁定了click事件,而不是為所有的button綁定事件,極大的提高了性能,這樣做的好處還有可以對動態加進來的元素進行處理
就是有一個按鈕原本的dom里面是沒有的,是你通過其他方式添加進來的,即未來元素,用直接綁定方法是不能成功的,只能用事件委托,委托給該元素的父級進行處理
由於事件委托是通過事件冒泡實現的,所以如果子級的元素阻止了事件冒泡,那么事件委托也將失效!
其實jQueryjQuery 1.7 Beta 1之前版本為綁定和委托事件提供了.bind()、.live()和.delegate()方法,可以說是一步步改進,
.bind()
假設有一個多行多列的表格,我們想讓用戶單擊每個單元格都能看到與其中內容相關的更多信息(比如,通過提示條)。為此,可以為每個單元格都綁定click事件:
$("info_table td").bind("click", function(){/*顯示更多信息*/});
問題是,如果表格中要綁定單擊事件的有10列500行,那么查找和遍歷5000個單元格會導致腳本執行速度明顯變慢,而保存5000個td元素和相應的事件處理程序也會占用大量內存
.live()
事件委托可以解決上述兩個問題。具體到代碼上,只要用jQuery 1.3新增的.live()方法代替.bind()方法即可:
$("#info_table td").live("click",function(){/*顯示更多信息*/});
這里的.live()方法會把click事件綁定到$(document)對象(但這一點從代碼中體現不出來,這也是.live()方法飽受詬病的一個重要原因,稍后再詳細討論),而且只需要給$(document)綁定一次(不是50次,更不是5000次)。在接收到任何事件時,$(document)對象都會檢查事件類型和事件目標,如果是click事件且事件目標是td,那么就執行委托給它的處理程序。
一切似乎很完美。可惜,事實並非如此。因為.live()方法並不完美,它有如下幾個主要缺點:
$()函數會找到當前頁面中的所有td元素並創建jQuery對象,但在確認事件目標時卻不用這個td元素集合,而是使用選擇符表達式與event.target或其祖先元素進行比較,因而生成這個jQuery對象會造成不必要的開銷;
默認把事件綁定到$(document)元素,如果DOM嵌套結構很深,事件冒泡通過大量祖先元素會導致性能損失;
只能放在直接選擇的元素后面,不能在連綴的DOM遍歷方法后面使用,即$("#infotable td").live...可以,但$("#infotable").find("td").live...不行;
收集td元素並創建jQuery對象,但實際操作的卻是$(document)對象,令人費解。
而為了避免事件冒泡造成的性能損失,jQuery從1.4開始支持在使用.live()方法時配合使用一個上下文參數:
$("td",$("#info_table")[0]).live("click",function(){/*顯示更多信息*/});
這樣,“受托方”就從默認的$(document)變成了$("#infotable")[0],節省了冒泡的旅程。
.delegate()
如前所述,為了突破單一.bind()方法的局限性,實現事件委托,jQuery 1.3引入了.live()方法。后來,為解決“事件傳播鏈”過長的問題,jQuery 1.4又支持為.live()方法指定上下文對象。而為了解決無謂生成元素集合的問題,jQuery 1.4.2干脆直接引入了一個新方法.delegate()。
使用.delegate(),前面的例子可以這樣寫:
$("#info_table").delegate("td","click",function(){/*顯示更多信息*/});
jQuery 1.7為了解決.bind()、.live()和.delegate()並存造成的不一致性問題,將會增加一對新的事件方法:.on()和.off():
$(elems).on(events, selector, data, fn);
$(elems).off(events, selector, fn);
如果指定selector,則為事件委托;否則,就是常規綁定。
所以現在只要用on方法就可以了,推薦用on方法進行委托或常規綁定事件