首頁 >web前端 >js教程 >在JS中如何實現動態添加元素

在JS中如何實現動態添加元素

亚连
亚连原創
2018-06-22 13:55:292379瀏覽

這篇文章主要為大家介紹了關於JS動態添加元素及綁定事件造成程序重複執行的相關資料,文中透過範例程式碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友們下面來一起看看吧。

前言

本文主要跟大家分享前段時間遇到的bug,這個Bug是關於jquery 的on方法綁定互動事件,類似於$('#point').on('click','.read-more',function () {})這樣的程式碼造成的程式重複執行,很多人在文章裡寫到了,也說了用off方法來解綁,但都未能點出問題的本質,幾乎都忽略了問題的本質其實是事件委託造成的。

話不多說,上點天天看到的程式碼:

第一種:  

 $(document).on('click', function (e) {
 consol.log('jquery事件绑定')
 });

第二種:  

 document.addEventListener('click',function (e) {
 consol.log('原生事件绑定')  
 });

#第三種:  

 var id = setInterval(function () {
 console.log('定时器循环事件绑定')
 },1000);

上面的程式碼,相信不少同盟,天天都會寫到,看似簡單的事件綁定,卻常常能為我們帶來意想不到的結果,特別是在這個SPA,應用AJAX頁面局部刷新如此盛行的時代。

那什麼是事件綁定,造成的程式重複執行呢?這個事情要說清除,好像不是那麼簡單,還是用一段測試程式碼來說明吧。你可以拷貝到本地,自己試試: 

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<button class="add_but">点击</button>
<p id="point">fdfsdf
</p>
<script src="https://cdn.bootcss.com/jquery/1.8.3/jquery.js"></script> 
<script>
 var count=1;
 var example = {
 getData:function () {
  var data ={
  content:&#39;df&#39;+count++,
  href:&#39;&#39;
  };
  this.renderData(data);
 },
 renderData:function (data) {
  document.getElementById(&#39;point&#39;).innerHTML=&#39;<p>this is a &#39;+data.content+&#39;点此<a class="read-more" href="javasript:;" rel="external nofollow" rel="external nofollow" >查看更多</a></p>&#39;;
  $(&#39;#point&#39;).on(&#39;click&#39;,&#39;.read-more&#39;,function () {
  alert(&#39;事故发生点&#39;);
 })
/*  setInterval(function () {
  console.log(&#39;fdfdfg&#39;);
  },2000);*/
  /*用冒泡来绑定事件,类似于Jquery的on绑定事件*/
 /* document.querySelector(&#39;body&#39;).addEventListener(&#39;click&#39;,function (e) {
  if(e.target.classList.contains(&#39;read-more&#39;)){
   alert(&#39;事故发生点&#39;);
  }
  })*/

 }
 } ;
 document.querySelector(&#39;.add_but&#39;).addEventListener(&#39;click&#39;,function (e) {
 example.getData();
 e.stopImmediatePropagation();
 });
</script>
</body>
</html>

以上是我為說清楚這個事情寫的一段測試程式碼,可以拷貝下來試試。當我們點選頁面的按鈕,觸發呼叫example.getData()這個函數,模擬ajax取得資料成功後,就會根據局部刷新頁面內元素類別名為point的內容,同時會為載入這個內容中的read-more A標籤綁定一個事件,就這樣我們想要的效果出現啦,當元素第一次加載時,頁面正常,'事故發生點'彈出一次,當二次刷新觸發後,你會發現其彈出了兩次,當第三次時,你會發現,其彈三次,以此類推。 。 。 。

OMG,這個程式到底怎麼了,我明明每次事件綁定前,前面綁定的元素都刪除了,為什麼,被刪除的屍體感覺還在動作,好吧,上面就是我第一次遇到這個情況發出的感嘆。

最後是問身邊的大神,才突然領悟,原來綁定一直都在,而這個綁定被保存在一個叫做事件隊列的地方,他不在循環執行的主線程中,畫了一張需要默契才能看懂的圖,勉強看。

事件佇列

還原真相

##其實上面那一段程式碼是為了測試而刻意寫的程式碼,除了計時器外,其他兩個點擊事件換個正常的寫法,重複執行的情況是不會出現的,正常的程式碼:  

 // jquery 事件直接绑定的写法;
 $(&#39;#point .read-more&#39;).on(&#39;click&#39;,function () {
  alert(&#39;事故发生点&#39;);
 })
 // 原生JS 事件直接绑定的写法;
 document.querySelector(&#39;.read-more&#39;).addEventListener(&#39;click&#39;,function (e) {
  alert(&#39;事故发生点&#39;);
 })

看出差別了嗎?其實就是不用冒泡來事件委託,而是直接給新增的元素綁定事件。所以

Dom事件是講道理的,動態加入的元素,再動態為此綁定事件,待元素被刪除後,與其綁定的對應事件其實是會從事件綁定佇列中刪除的 ,而非如上面測試程式碼,給人的感覺是元素移除後,但其綁定的事件還在記憶體中。 但請記住,這是個誤會,上面測試的程式碼之所以給人這種錯覺,是因為我們並沒有為動態添加的元素綁定事件,而僅僅是用了事件委託的形式,實際上事件是綁定在#point元素上的,其一直存在,利用事件冒泡來讓程式知道我們點擊了動態添加的連結元素。測試中刻意用原生js去重現了這次事件委託,jquery的on綁定事件其實原理基本上相同。

document.querySelector(&#39;body&#39;).addEventListener(&#39;click&#39;,function (e) {
 if(e.target.classList.contains(&#39;read-more&#39;)){
  alert(&#39;事故发生点&#39;);
 }
})

解除bug的那些方法

#計時器##這個是最容易犯的錯誤,當然也是最易解的錯誤,因為設定定時器時,其會傳回一個數值,這個數值應該是事件隊列此定時器中的一個編號吧,類似於9527;步驟就是設定一個全域變數來保持這個回傳值id,每次設定計時器時,先透過id清除已經設定過的定時器    

 clearInterval(intervalId); //粗暴的写法
 intervalId&&clearInterval(intervalId); //严谨的写法
 intervalId=setInterval(function () {
  console.log(&#39;fdfdfg&#39;);
  },2000);

Dom事件

#其實上面我們已經說過,最直接的辦法就是不採用事件委託,而是採用直接綁定;如果確實要用事件委託來綁定事件,那就是解綁定。在jquery中提供了unbind函數來解綁事件,不過在jquery 1.8版本以後,這個方法已經不推薦了,而是推薦off方法。例如上面的on事件委託的方式,要解綁,可採用語句

$('#point').off('click','.read-more')

<p><strong>有缺陷的解决方案,添加flag</strong></p> <p>很好理解,第一次绑定后,flag置位,下一次在执行这个绑定时,程序就知道在这个节点上已经有了绑定,无需再添加,具体操作就是:  </p><pre class="brush:php;toolbar:false;"> var flag = false; var example = { getData: function () { var data = { content: &amp;#39;df&amp;#39; + count++, href: &amp;#39;&amp;#39; }; this.renderData(data); }, renderData: function (data) { document.getElementById(&amp;#39;point&amp;#39;).innerHTML = &amp;#39;&lt;p&gt;this is a &amp;#39; + data.content + &amp;#39;点此&lt;a class=&quot;read-more&quot; href=&quot;javasript:;&quot; rel=&quot;external nofollow&quot; rel=&quot;external nofollow&quot; &gt;查看更多&lt;/a&gt;&lt;/p&gt;&amp;#39;; !flag &amp;&amp; $(&amp;#39;#point&amp;#39;).on(&amp;#39;click&amp;#39;, &amp;#39;.read-more&amp;#39;, function () { alert(&amp;#39;事故发生点&amp;#39;+data.content); }); flag = true; } };</pre><p>从逻辑上,看起来没有问题,但仔细观察,发现这是有问题的。当我们第二次,第三次刷新时,弹出框的内容还是和第一次模拟刷新后点击后弹出的内容一致,还是'事故发生点df1',而非和内容一样递增,为什么呢,感觉事件队列里面的回调函数被单独保存起来了,data被深拷贝了,而不再是一个引用。确实有点难理解,我也不知道到底是为什么,如果哪位能说清楚,还请一定告知。</p> <p>结个尾写在最后,其实平常写一些程序时,事件绑定,造成程序重复执行这些情况很少发生,其通常会出现在我们写插件的时候,插件需要适应多种调用环境,所以在插件内部做到防止事件重复绑定的情况非常重要。</p> <p>上面是我整理给大家的,希望今后会对大家有帮助。</p> <p>相关文章:</p> <p><a href="http://www.php.cn/js-tutorial-404480.html" target="_blank">使用node.js如何创建子进程(详细教程)</a></p> <p><a href="http://www.php.cn/js-tutorial-404478.html" target="_blank">使用ES6如何实现单例模式</a></p> <p><a href="http://www.php.cn/js-tutorial-404476.html" target="_blank">如何把Angular项目部署到nginx上</a></p> <p><a href="http://www.php.cn/js-tutorial-404474.html" target="_blank">在vue中使用v-model如何实现父子组件通信</a></p> <p><a href="http://www.php.cn/js-tutorial-404471.html" target="_blank">在react中有关组件通信有哪些方法?</a></p>

以上是在JS中如何實現動態添加元素的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn