JSで要素を動的に追加する方法

亚连
亚连オリジナル
2018-06-22 13:55:292385ブラウズ

この記事では、JS での要素の動的追加とイベントのバインディングによるプログラムの繰り返し実行に関する関連情報を、サンプル コードを通じて詳しく紹介します。この記事は、あらゆる人の学習や仕事に役立つ特定の学習価値があります。それは必要です、友達、一緒に見てみましょう。

前書き

この記事では主に、少し前に発生したバグについて説明します。このバグは、$('#point').on(' click と同様の、jquery の on メソッド バインディング インタラクティブ イベントに関するものです。 ','.read-more',function () {}) このようなコードにより、プログラムが繰り返し実行されます。これについては多くの人が記事で書いており、バインドを解除するための off メソッドの使用についても言及しています。問題の本質を指摘できても、問題の本質が実際にイベント委任によって引き起こされているということはほとんど無視されます。 $('#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')

🎜🎜 早速、私が毎日目にするコードを以下に示します。 🎜🎜🎜🎜🎜 1 つ目: 🎜🎜
 var flag = false;
 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;;
  !flag && $(&#39;#point&#39;).on(&#39;click&#39;, &#39;.read-more&#39;, function () {
  alert(&#39;事故发生点&#39;+data.content);
  });
  flag = true;
 }
 };
🎜🎜 2 つ目: 🎜🎜rrreee🎜🎜 3 つ目: 🎜🎜rrreee🎜上記のコードについては、多くのアライアンスが毎日このことについて書いていると思います。特に SPA と AJAX ページの部分更新が非常に普及しているこの時代では、一見単純なイベント バインディングが予期せぬ結果をもたらすことがよくあります。 🎜🎜では、イベント バインディングとは何ですか?また、何がプログラムの繰り返し実行を引き起こすのでしょうか?この問題をクリアするのはそれほど簡単ではないようなので、テストコードを使用して説明してみましょう。ローカルにコピーして自分で試すことができます: 🎜rrreee🎜 上記は、この問題を明確にするために私が書いたテストコードです。コピーして試してください。ページ上のボタンをクリックすると、関数 example.getData() が呼び出され、Ajax をシミュレートしてデータを正常に取得した後、ページ内の point という名前の要素クラスのコンテンツが呼び出されます。このコンテンツの A タグはイベントにバインドされており、要素が初めて読み込まれるときに、必要な効果が表示されます。ページは正常で、2 回目の更新がトリガーされると「事故ポイント」が 1 回ポップアップし、3 回目では 3 回ポップアップすることがわかります。 。 。 。 🎜🎜このプログラムの何が問題なのでしょうか? 各イベントがバインドされる前に、以前にバインドされた要素が削除されることはわかっています。なぜなら、削除された死体がまだ動いているように見えるのです。この状況からの感嘆の声。 🎜🎜最後に、周りのマスターに尋ねたところ、バインディングは常に存在しており、このバインディングはイベントキューと呼ばれる場所に保存されており、絵を描くために必要なメインスレッドには存在しませんでした。暗黙の了解で絵を見てやっと理解できる。 🎜

🎜

イベント キュー🎜🎜🎜🎜真実を復元します🎜🎜🎜🎜実際、上記のコードはタイマーに加えて、他の 2 つのクリック イベントもテスト用に特別に書かれています。通常の記述では、繰り返し実行は発生しません。 通常のコード: 🎜rrreee🎜 実際、イベント デリゲートをバブルアップする必要はなく、追加された要素にイベントを直接バインドする必要があります。したがって、動的に追加された要素の場合、イベントはこの要素に動的にバインドされ、その要素にバインドされた対応するイベントは、上記のテスト コードのようにではなく、実際にイベント バインディング キューから削除されます。要素が削除された後も、バインドされたイベントがまだメモリ内にあるような印象を与えます。 🎜ただし、覚えておいてください🎜、これは誤解です。上記でテストしたコードがこのような錯覚を引き起こす理由は、イベントを動的に追加された要素にバインドせず、実際にはイベント委任の形式のみを使用しているためです。 #point 要素は常に存在し、イベント バブリングを使用して、動的に追加されたリンク要素をクリックしたことをプログラムに知らせます。テストでは、イベント委任を再現するためにネイティブ js が意図的に使用されました。jquery のバインディング イベントの原理は基本的に同じです。 🎜rrreee🎜🎜🎜バグを排除するためのメソッド🎜🎜🎜🎜🎜タイマー🎜🎜🎜これは最もよくある間違いであり、もちろん、解決するのが最も簡単です。なぜなら、タイマーが設定されると、この値が返されるからです。 9527 と同様に、イベント キューのタイマー内の数値である必要があります。このステップでは、タイマーが設定されるたびに、最初にタイマー 🎜rrreee を通じて設定値をクリアします。 🎜🎜Dom イベント🎜🎜🎜 実際、上で述べたように、最も直接的な方法は、イベント委任を使用するのではなく、直接バインディングを使用することです。イベント委任を使用してイベントをバインドしたい場合は、 unbind を使用します。 jquery ではイベントをバインド解除するための unbind 関数が提供されていますが、jquery バージョン 1.8 以降では、この方法は推奨されなくなり、代わりに off 方法が推奨されます。たとえば、上記のイベント委任メソッドでバインドを解除するには、ステートメント $('#point').off('click','.read-more') を使用できます。 🎜

有缺陷的解决方案,添加flag

很好理解,第一次绑定后,flag置位,下一次在执行这个绑定时,程序就知道在这个节点上已经有了绑定,无需再添加,具体操作就是:  

 var flag = false;
 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;;
  !flag && $(&#39;#point&#39;).on(&#39;click&#39;, &#39;.read-more&#39;, function () {
  alert(&#39;事故发生点&#39;+data.content);
  });
  flag = true;
 }
 };

从逻辑上,看起来没有问题,但仔细观察,发现这是有问题的。当我们第二次,第三次刷新时,弹出框的内容还是和第一次模拟刷新后点击后弹出的内容一致,还是'事故发生点df1',而非和内容一样递增,为什么呢,感觉事件队列里面的回调函数被单独保存起来了,data被深拷贝了,而不再是一个引用。确实有点难理解,我也不知道到底是为什么,如果哪位能说清楚,还请一定告知。

结个尾写在最后,其实平常写一些程序时,事件绑定,造成程序重复执行这些情况很少发生,其通常会出现在我们写插件的时候,插件需要适应多种调用环境,所以在插件内部做到防止事件重复绑定的情况非常重要。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用node.js如何创建子进程(详细教程)

使用ES6如何实现单例模式

如何把Angular项目部署到nginx上

在vue中使用v-model如何实现父子组件通信

在react中有关组件通信有哪些方法?

以上がJSで要素を動的に追加する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。