Jquery通過append新元素之後事件繫結問題的解決方案
昨天在專案中發現一個問題:在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');
});
});