欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

on()方法绑定的事件执行两次的原因

程序员文章站 2024-02-04 20:42:58
on()方法绑定的事件执行两次的原因 前提 今天在测试功能时,突然发现为 ul 中的 li元素 绑定的点击事件 “失效了”。即点击了没反应。 具体是这...

on()方法绑定的事件执行两次的原因

前提

今天在测试功能时,突然发现为 ul 中的 li元素 绑定的点击事件 “失效了”。即点击了没反应。

具体是这样的:

页面中本来就存在一个 ul 列表,然后 为 ul 中的 每一个 li 元素 用 on()方法 绑定了点击事件。 此外,页面中还有一个新增按钮,点击可以在页面中 追加 一个 ul 列表 。 追加之后,也为 新 ul 列表 中的 每一个 li 元素 绑定了点击事件。同样,用的也是 on() 方法。

问题来了:

在点击 新增 ul 列表之前,为 ul 中的 li 元素 绑定的点击事件还可以正常使用;

在 新增 ul 列表之后,再次点击之前 ul 中的 li 元素 ,你会发现:点击之后没反应。

通过调试代码才发现,原来是因为点击事件执行了两次,导致样式增加后又被移除。所以看起来就好像 点击事件失效了一样。

功能问题的动图演示如下所示:

on()方法绑定的事件执行两次的原因

出现问题的原因:

那么,为什么会出现这种问题呢? 刚开始我也很好奇,感觉 可能是因为自己绑定的方式有问题,所以只好自己写个 demo 自行测试一下。

情景还原

首先,页面 html 当中本来就存在一个 静态的ul列表。【如下图所示】

  • 我是li1
  • 我是li2
  • 我是li3
  • 我是li4
  • 我是li5

on()方法绑定的事件执行两次的原因

然后,在页面加载完成之后,需要为 ul 中的每一个 li 元素添加点击事件。来实现点击选中,再次点击取消的效果。
<script>
	$("ul").on("click","li",function(){
		$(this).toggleclass('item');
	})
</script>
点击 新增按钮 之后,页面中又追加了一个 新的 ul 列表。【如下图所示】

我平时遇到的比较多的情况是:在追加时就为该元素绑定了点击事件。具体如下代码所示:

//点击在 body 里  追加一个 ul 列表
$("button").on("click",function(){
	$("body").append("
  • "+ "
  • 我是li new 1
  • "+ "
  • 我是li new 2
  • "+ "
  • 我是li new 3
  • "+ "
"); }); function change(obj){ $(obj).toggleclass('item'); } 这样做其实也没错,缺点就是太麻烦了,需要你把每个元素都加一遍点击事件。 如果只有一个元素还好办,绑定一次就好了。 但是你要是碰到 ul li 列表这样的,就需要给每一个 li 元素 都绑定点击事件,那就太麻烦了。 还不如直接在追加完 ul 列表之后,统一给 li 元素 绑定事件,这不就行了? 简单快捷,多方便。

于是,为简单方便,我就直接这样写了。具体如下代码所示:

//点击在 body 里  追加一个 ul 列表
$("button").on("click",function(){
	$("body").append("
  • "+ "
  • 我是li new 1
  • "+ "
  • 我是li new 2
  • "+ "
  • 我是li new 3
  • "+ "
"); //然后也需要为新增的 ul 列表中的 每一个 li 元素添加点击事件 $("ul").on("click","li",function(){ $(this).toggleclass('item'); }); });

-on()方法绑定的事件执行两次的原因

刚开始没怎么注意,不知道这样写会存在什么样的问题。 等后面测试功能的时候,我才发现了问题所在:li 元素在点击之后一点反应都没有,点了 等于 没点,样式也没变化。 后来打断点调试才发现:原来是因为 li 元素 的点击事件执行了两次。

为什么会执行两次呢?
答案马上揭晓…

在页面加载完成之后,就为页面上 已经存在的 ul 列表中的 每一个 li 元素 添加了点击事件。没错吧~
on()方法绑定的事件执行两次的原因

此时是没有任何问题的。然后,我们 点击 “新增” 按钮,页面上就会追加 一个 新的 ul 列表。
on()方法绑定的事件执行两次的原因

追加完成之后呢,又会给 新增 的 ul 列表 中的 每一个 li 元素 添加点击事件。

注意:

仔细看,此时为 每一个 li 元素 添加点击事件的 父元素 ul ,居然有两个。

我的本意是 只给新增的 ul 列表 中的每一个 li 元素 添加点击事件,没想到点击按钮之后,又为 原有的 ul 列表 中的 每一个 li 元素 添加了一次点击事件。

这么算来,原有的 ul 列表 中的 每一个 li 元素 就被添加了 两次 点击事件,所以每点一次就会出现执行两次的现象。
on()方法绑定的事件执行两次的原因

接下来,我们点击 原有的 ul 列表中的某一个 li 元素 试试,看一下效果。
on()方法绑定的事件执行两次的原因

执行下一步,完成点击事件

on()方法绑定的事件执行两次的原因

继续往下执行,看看会发生什么?

on()方法绑定的事件执行两次的原因

然后你会惊奇的发现:第 3个 li 元素的样式消失了…

on()方法绑定的事件执行两次的原因

现在去尝试点击一下,看一看 新旧 li 元素在被点击之后有什么不同之处。

on()方法绑定的事件执行两次的原因

分析:

没错吧,新增的 li元素点击事件不受影响,而原有的 li元素 就没有那么幸运了。 因为原有的 li元素被添加了两次点击事件,所以就会点一次执行两次,样式添加之后又被移除了。整个过程就相当于没有点击,最后看起来就好像 “点击事件失效了” 一样。

解决问题

那么,如何去解决呢? 很简单,在绑定之前先解绑就可以了。 原理:在第二次为 li 元素添加点击事件之前,先将最开始为 li 元素添加的点击事件移除掉,然后重新添加一次点击事件即可。 这样做,既不影响最初时元素的点击功能,也不会在新增该元素之后,为新增元素添加点击事件时,又为原有的元素重复添加点击事件。 具体操作如下:
$("ul").off("click","li").on("click","li",function(){
	$(this).toggleclass('item');
});

现在我终于明白了:为什么在绑定事件之前还需要专门解绑一次事件。以前虽然经常看到这种操作,但是不明白这么做的原因,也不知道什么情况下需要先解绑再绑定事件。

结束语

一开始我百度了一下,还以为是因为冒泡的原因。当时百度的关键词是:子元素的点击事件触发两次

搜到的结果是这样的,于是就点开看了下第一个搜索结果,看看有没有什么帮助:
on()方法绑定的事件执行两次的原因

原文中作者也遇到了和我同样的问题:点击事件执行两次,但是针对他自己的情况,他给出的结论是:不是事件绑定而是因为事件冒泡。 然后我以为,我的问题也是因为事件冒泡的原因。然后就试着加了 return false 或者event.stoppropagation(),发现并没有效果。 后来经过自己打断点调试,才定位到问题所在。 我们都遇到了同样的问题,但我的问题确实是事件绑定的原因。而他问题的元凶则是事件冒泡。

参考资料:js(jquery)的on绑定点击事件执行两次的解决办法

至此,问题就已经完美解决了。

附录

演示 demo 的完整代码,如下所示:




<script src="jquery-3.2.1.min.js"></script>

  • 我是li1
  •  
  • 我是li2
  • 我是li3
  • 我是li4
  • 我是li5
<script> $("ul").on("click","li",function(){ $(this).toggleclass('item'); }) /*$("li").on('click', function(event) { $(this).toggleclass('item'); });*/ $("button").on("click",function(){ $("body").append("
  • 我是li new 1
  • 我是li new 2
  • 我是li new 3
"); $("ul").off("click","li").on("click","li",function(){ $(this).toggleclass('item'); }); // $("ul").on("click","li",function(){ // $(this).toggleclass('item'); // }); }); </script>