JS動(dòng)態(tài)添加元素及綁定事件造成程序重復(fù)執(zhí)行解決
前言
本文主要給大家分享一下前段時(shí)間遇到的bug,這個(gè)Bug是關(guān)于jquery 的on方法綁交互事件,類似于$('#point').on('click','.read-more',function () {})這樣的代碼造成的程序重復(fù)執(zhí)行,很多人在文章里寫到了,也說了用off方法來解綁,但都未能點(diǎn)出問題的本質(zhì),幾乎都忽略了問題的本質(zhì)其實(shí)是事件委托造成的。
話不多說,上點(diǎn)天天看到的代碼:
第一種:
$(document).on('click', function (e) {
consol.log('jquery事件綁定')
});
第二種:
document.addEventListener('click',function (e) {
consol.log('原生事件綁定')
});
第三種:
var id = setInterval(function () {
console.log('定時(shí)器循環(huán)事件綁定')
},1000);
上面的代碼,相信不少同盟,天天都會(huì)寫到,看似簡(jiǎn)單的事件綁定,卻經(jīng)常能給我們帶來意想不到的結(jié)果,特別是在這個(gè)SPA,應(yīng)用AJAX頁(yè)面局部刷新如此盛行的時(shí)代。
那什么是事件綁定,造成的程序重復(fù)執(zhí)行呢?這個(gè)事情要說清除,好像不是那么簡(jiǎn)單,還是用一段測(cè)試代碼來說明吧。你可以拷貝到本地,自己試試:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button class="add_but">點(diǎn)擊</button>
<div id="point">fdfsdf
</div>
<script src="https://cdn.bootcss.com/jquery/1.8.3/jquery.js"></script>
<script>
var count=1;
var example = {
getData:function () {
var data ={
content:'df'+count++,
href:''
};
this.renderData(data);
},
renderData:function (data) {
document.getElementById('point').innerHTML='<div>this is a '+data.content+'點(diǎn)此<a class="read-more" href="javasript:;" rel="external nofollow" rel="external nofollow" >查看更多</a></div>';
$('#point').on('click','.read-more',function () {
alert('事故發(fā)生點(diǎn)');
})
/* setInterval(function () {
console.log('fdfdfg');
},2000);*/
/*用冒泡來綁定事件,類似于Jquery的on綁定事件*/
/* document.querySelector('body').addEventListener('click',function (e) {
if(e.target.classList.contains('read-more')){
alert('事故發(fā)生點(diǎn)');
}
})*/
}
} ;
document.querySelector('.add_but').addEventListener('click',function (e) {
example.getData();
e.stopImmediatePropagation();
});
</script>
</body>
</html>
以上是我為說清這個(gè)事情寫的一段測(cè)試代碼,可以拷貝下來試試。當(dāng)我們點(diǎn)擊頁(yè)面的按鈕,觸發(fā)調(diào)用example.getData()這個(gè)函數(shù),模擬ajax獲取數(shù)據(jù)成功后,就會(huì)根據(jù)局部刷新頁(yè)面內(nèi)元素類名為point的內(nèi)容,同時(shí)會(huì)為加載這個(gè)內(nèi)容中的read-more A標(biāo)簽綁定一個(gè)事件,就這樣我們想要的效果出現(xiàn)啦,當(dāng)元素第一次加載時(shí),頁(yè)面正常,‘事故發(fā)生點(diǎn)'彈出一次,當(dāng)二次刷新觸發(fā)后,你會(huì)發(fā)現(xiàn)其彈出了兩次,當(dāng)?shù)谌螘r(shí),你會(huì)發(fā)現(xiàn),其彈三次,以此類推。。。。
OMG,這個(gè)程序到底怎么了,我明明每次事件綁定前,前面綁定的元素都刪除了,為什么,被刪除的尸體感覺還在動(dòng)作,好吧,上面就是我第一次遇到這個(gè)情況發(fā)出的感嘆。
最后是問身邊的大神,才突然領(lǐng)悟,原來綁定一直都在,而這個(gè)綁定被保存在一個(gè)叫做事件隊(duì)列的地方,他不在循環(huán)執(zhí)行的主線程中,畫了一張需要默契才能看懂的圖,勉強(qiáng)看一看。

事件隊(duì)列
還原真相
其實(shí)上面那一段代碼是為了測(cè)試而特意寫的代碼,除了定時(shí)器外,其他兩個(gè)點(diǎn)擊事件換個(gè)正常的寫法,重復(fù)執(zhí)行的情況是不會(huì)出現(xiàn)的,正常的代碼:
// jquery 事件直接綁定的寫法;
$('#point .read-more').on('click',function () {
alert('事故發(fā)生點(diǎn)');
})
// 原生JS 事件直接綁定的寫法;
document.querySelector('.read-more').addEventListener('click',function (e) {
alert('事故發(fā)生點(diǎn)');
})
看出差別了嗎?其實(shí)就是不用冒泡來事件委托,而是直接給添加的元素綁定事件。所以Dom事件是講道理的,動(dòng)態(tài)添加的元素,再動(dòng)態(tài)為此綁定事件,待元素被刪除后,與其綁定的相應(yīng)事件其實(shí)是會(huì)從事件綁定隊(duì)列中刪除的,而非如上面測(cè)試代碼,給人的感覺是元素移除后,但其綁定的事件還在內(nèi)存中。但請(qǐng)記住,這是個(gè)誤會(huì),上面測(cè)試的代碼之所以給人這種錯(cuò)覺,是因?yàn)槲覀儾]有為動(dòng)態(tài)添加的元素綁定事件,而僅僅是用了事件委托的形式,實(shí)際上事件是綁定在#point元素上的,其一直存在,利用事件冒泡來讓程序知道我們點(diǎn)擊了動(dòng)態(tài)添加的鏈接元素。測(cè)試中特意用原生js去重現(xiàn)了這次事件委托,jquery的on綁定事件其實(shí)原理基本相同。
document.querySelector('body').addEventListener('click',function (e) {
if(e.target.classList.contains('read-more')){
alert('事故發(fā)生點(diǎn)');
}
})
解除bug的那些方法
定時(shí)器
這個(gè)是最易犯的錯(cuò)誤,當(dāng)然也是最易解的錯(cuò)誤,因?yàn)樵O(shè)定定時(shí)器時(shí),其會(huì)返回一個(gè)數(shù)值,這個(gè)數(shù)值應(yīng)該是事件隊(duì)列此定時(shí)器中的一個(gè)編號(hào)吧,類似于9527;步驟就是設(shè)定一個(gè)全局變量來保持這個(gè)返回值id,在每次設(shè)定定時(shí)器時(shí),先通過id清除已經(jīng)設(shè)定過的定時(shí)器
clearInterval(intervalId); //粗暴的寫法
intervalId&&clearInterval(intervalId); //嚴(yán)謹(jǐn)?shù)膶懛?
intervalId=setInterval(function () {
console.log('fdfdfg');
},2000);
Dom事件
其實(shí)上面我們已經(jīng)說過,最直接的辦法就是不采用事件委托,而是采用直接綁定;如果確實(shí)要用事件委托來綁定事件,那就是解綁。在jquery中提供了unbind函數(shù)來解綁事件,不過在jquery 1.8版本以后,這個(gè)方法已經(jīng)不推薦了,而是推薦off方法。比如上面的on事件委托的方式,要解綁,可采用語(yǔ)句$('#point').off('click','.read-more') 。
有缺陷的解決方案,添加flag
很好理解,第一次綁定后,flag置位,下一次在執(zhí)行這個(gè)綁定時(shí),程序就知道在這個(gè)節(jié)點(diǎn)上已經(jīng)有了綁定,無需再添加,具體操作就是:
var flag = false;
var example = {
getData: function () {
var data = {
content: 'df' + count++,
href: ''
};
this.renderData(data);
},
renderData: function (data) {
document.getElementById('point').innerHTML = '<div>this is a ' + data.content + '點(diǎn)此<a class="read-more" href="javasript:;" rel="external nofollow" rel="external nofollow" >查看更多</a></div>';
!flag && $('#point').on('click', '.read-more', function () {
alert('事故發(fā)生點(diǎn)'+data.content);
});
flag = true;
}
};
從邏輯上,看起來沒有問題,但仔細(xì)觀察,發(fā)現(xiàn)這是有問題的。當(dāng)我們第二次,第三次刷新時(shí),彈出框的內(nèi)容還是和第一次模擬刷新后點(diǎn)擊后彈出的內(nèi)容一致,還是'事故發(fā)生點(diǎn)df1',而非和內(nèi)容一樣遞增,為什么呢,感覺事件隊(duì)列里面的回調(diào)函數(shù)被單獨(dú)保存起來了,data被深拷貝了,而不再是一個(gè)引用。確實(shí)有點(diǎn)難理解,我也不知道到底是為什么,如果哪位能說清楚,還請(qǐng)一定告知。
結(jié)個(gè)尾寫在最后,其實(shí)平常寫一些程序時(shí),事件綁定,造成程序重復(fù)執(zhí)行這些情況很少發(fā)生,其通常會(huì)出現(xiàn)在我們寫插件的時(shí)候,插件需要適應(yīng)多種調(diào)用環(huán)境,所以在插件內(nèi)部做到防止事件重復(fù)綁定的情況非常重要。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- JavaScript實(shí)現(xiàn)父子dom同時(shí)綁定兩個(gè)點(diǎn)擊事件,一個(gè)用捕獲,一個(gè)用冒泡時(shí)執(zhí)行順序的方法
- JavaScript call apply使用 JavaScript對(duì)象的方法綁定到DOM事件后this指向問題
- js中DOM事件綁定分析
- JS 事件綁定、事件監(jiān)聽、事件委托詳細(xì)介紹
- JavaScript給按鈕綁定點(diǎn)擊事件(onclick)的方法
- js創(chuàng)建一個(gè)input數(shù)組并綁定click事件的方法
- javascript事件委托的方式綁定詳解
- Javascript 事件流和事件綁定
- javaScript 事件綁定、事件冒泡、事件捕獲和事件執(zhí)行順序整理總結(jié)
- JavaScript 事件屬性綁定帶參數(shù)的函數(shù)
- js事件on動(dòng)態(tài)綁定數(shù)據(jù),綁定多個(gè)事件的方法
- JavaScript Dom 綁定事件操作實(shí)例詳解
相關(guān)文章
JS實(shí)現(xiàn)為動(dòng)態(tài)添加的元素增加事件功能示例【基于事件委托】
這篇文章主要介紹了JS實(shí)現(xiàn)為動(dòng)態(tài)添加的元素增加事件功能,結(jié)合實(shí)例形式分析了javascript基于事件委托實(shí)現(xiàn)針對(duì)動(dòng)態(tài)添加的元素增加事件的相關(guān)操作技巧,需要的朋友可以參考下2018-03-03
深入學(xué)習(xí)JavaScript執(zhí)行上下文
這篇文章主要介紹了深入學(xué)習(xí)JavaScript執(zhí)行上下文,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助2022-08-08
淺談JavaScript中Date(日期對(duì)象),Math對(duì)象
這篇文章主要簡(jiǎn)單介紹了JavaScript中Date(日期對(duì)象),Math對(duì)象的相關(guān)資料,需要的朋友可以參考下2015-02-02
Postman自動(dòng)化接口測(cè)試實(shí)戰(zhàn)
有時(shí)我們可能需要在多個(gè)環(huán)境下對(duì)同一個(gè)接口進(jìn)行測(cè)試。比如我們請(qǐng)求的域名,開發(fā)、測(cè)試、生產(chǎn)環(huán)境,請(qǐng)求參數(shù)。文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
詳解JavaScript如何創(chuàng)建一個(gè)非自動(dòng)播放的GIF網(wǎng)絡(luò)組件
這篇文章主要為大家介紹了如何利用JavaScript創(chuàng)建一個(gè)允許您的用戶決定是否要播放gif的Web組件,文中的實(shí)現(xiàn)步驟講解詳細(xì),需要的可以參考一下2022-02-02
基于BootStrap柵格欄系統(tǒng)完成網(wǎng)站底部版權(quán)信息區(qū)
網(wǎng)站底部版權(quán)信息區(qū)可以用bootstrap的“柵格系統(tǒng)”完成,下面給大家分享一個(gè)未經(jīng)處理的底部版權(quán)信息區(qū)的樣式,大家可以做個(gè)參考2016-12-12
JS圖片根據(jù)鼠標(biāo)滾動(dòng)延時(shí)加載的實(shí)例代碼
這篇文章介紹了,JS圖片根據(jù)鼠標(biāo)滾動(dòng)延時(shí)加載的實(shí)例代碼,有需要的朋友可以參考一下2013-07-07
Bootstrap響應(yīng)式側(cè)邊欄改進(jìn)版
這篇文章主要為大家詳細(xì)介紹了Bootstrap響應(yīng)式側(cè)邊欄改進(jìn)版,結(jié)合導(dǎo)航條和下拉菜單進(jìn)行改進(jìn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09

