昨天在項目中發現一個問題:在DOM加載之後為标簽綁定的事件對于新加進來的标簽并不起作用,通過查找發現事件并沒有綁定到新加入的标簽,是以今天特意總結一下這種問題的解決方案。
在jquery中,我們通常是在DOM加載完成後,再對元素綁定事件。以下面的情景為例:制作一個表格,每一欄最後有個button可以删除這一欄。然後還有一個添加按鈕,可以給表格添加一欄。代碼如下:
HTML代碼:
<table>
<thead>
<tr>
<th>學号</th>
<th>成績</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>100</td>
<td>98</td>
<td><button class="del">删除</button></td>
</tr>
<tr>
<td>101</td>
<td>88</td>
<td><button class="del">删除</button></td>
</tr>
<tr>
<td>102</td>
<td>78</td>
<td><button class="del">删除</button></td>
</tr>
</tbody>
</table>
<button class="add">添加</button>
jquery代碼:
$(function(){
$('.del').click(function(){
$(this).parents("tr").remove();
});
$('.add').click(function() {
$("<tr><td>103</td><td>68</td><td><button class='del'>删除</button></td></tr>").appendTo('tbody');
});
});
上述代碼在DOM加載完後,通過jquery向
class="del"
的删除按鈕添加了一個click事件,用于删除該欄。通過運作測試我們可以發現,給删除按鈕添加的點選事件對DOM中存在的前三欄是起作用,但是如果我們再點選添加按鈕添加一欄,則新增的這一欄中的删除按鈕并沒有綁定點選事件。
js的事件監聽跟css不一樣,我們知道css隻要設定好了樣式,不論是原來就有的還是新添加的,都有一樣的表現。而事件監聽不是,新增加的雖然設定了相同的class,但是并沒有綁定事件,你必須給每一個元素單獨綁定事件。
這種問題的處理方法主要有三種:
第一種:重複綁定
重複綁定法:DOM加載時,先對DOM中存在的元素進行事件綁定,每次新增的元素時,再對新增元素綁定一次事件,jquery代碼如下:
$(function(){
$('.del').click(deleteTr);
$('.add').click(function() {
$("<tr><td>103</td><td>68</td><td><button class='del'>删除</button></td></tr>").find(".del").click(deleteTr).end().appendTo('tbody');
});
function deleteTr(){
$(this).parents("tr").remove();
}
});
第二種:使用onclick
這種方法就是直接在标簽上添加onclick屬性,如果不考慮結構與行為分離的準則,這個方法也是能達到效果的:具體代碼如下:
HTML代碼:
<table>
<thead>
<tr>
<th>學号</th>
<th>成績</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>100</td>
<td>98</td>
<td><button class="del" onclick='deleteTr(this)'>删除</button></td>
</tr>
<tr>
<td>101</td>
<td>88</td>
<td><button class="del" onclick='deleteTr(this)'>删除</button></td>
</tr>
<tr>
<td>102</td>
<td>78</td>
<td><button class="del" onclick='deleteTr(this)'>删除</button></td>
</tr>
</tbody>
</table>
<button class="add">添加</button>
jquery代碼:
$(function(){
$('.add').click(function() {
$("<tr><td>103</td><td>68</td><td><button class='del' onclick='deleteTr(this)'>删除</button></td></tr>").appendTo('tbody');
});
});
function deleteTr(btn){
$(btn).parents("tr").remove();
}
這裡有一點需要注意就是
onclick='deleteTr(this)'
中的
deleteTr()
函數不能定義在
$(function(){});
中,如果定義在
$(function(){});
中,在解析HTML時,無法調用,是以需要定義在外面。
第三種:事件委托
通過檢視jquery API文檔,發現有個live()事件,對live()事件的概述如下:“jQuery 給所有比對的元素附加一個事件處理函數,即使這個元素是以後再添加進來的也有效。”但是很不幸的是,live()在jquery 1.7之後就不推薦使用了。
是以我們需要另外找路,通過檢視文檔我們發現:.live() 方法能對一個還沒有添加進DOM的元素有效,是由于使用了事件委托:綁定在祖先元素上的事件處理函數可以對在後代上觸發的事件作出回應。傳遞給 .live() 的事件處理函數不會綁定在元素上,而是把他作為一個特殊的事件處理函數,綁定在 DOM 樹的根節點上。
既然知道了原理,那麼我們可以自己實作事件委托,我們不在button上直接綁定事件,而在把事件綁定在tbody上,通過判斷事件的目标event.target對象的某些屬性來判斷這個對象是不是我們要找的事件觸發的對象。
下面,我們通過className來判斷是否是我們的目标對象:
$(function(){
$('tbody').click(function(event){
if (event.target.className == "del") {
$(event.target).parents("tr").remove();
}
});
$('.add').click(function() {
$("<tr><td>103</td><td>68</td><td><button class='del'>删除</button></td></tr>").appendTo('tbody');
});
});