on()方法绑定的事件执行两次的原因
on()方法绑定的事件执行两次的原因
前提
今天在测试功能时,突然发现为 ul 中的 li元素 绑定的点击事件 “失效了”。即点击了没反应。具体是这样的:
页面中本来就存在一个 ul 列表,然后 为 ul 中的 每一个 li 元素 用 on()方法 绑定了点击事件。 此外,页面中还有一个新增按钮,点击可以在页面中 追加 一个 ul 列表 。 追加之后,也为 新 ul 列表 中的 每一个 li 元素 绑定了点击事件。同样,用的也是 on() 方法。问题来了:
在点击 新增 ul 列表之前,为 ul 中的 li 元素 绑定的点击事件还可以正常使用;
在 新增 ul 列表之后,再次点击之前 ul 中的 li 元素 ,你会发现:点击之后没反应。
通过调试代码才发现,原来是因为点击事件执行了两次,导致样式增加后又被移除。所以看起来就好像 点击事件失效了一样。
功能问题的动图演示如下所示:
出现问题的原因:
那么,为什么会出现这种问题呢? 刚开始我也很好奇,感觉 可能是因为自己绑定的方式有问题,所以只好自己写个 demo 自行测试一下。情景还原
首先,页面 html 当中本来就存在一个 静态的ul列表。【如下图所示】- 我是li1
- 我是li2
- 我是li3
- 我是li4
- 我是li5
<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
- "+ "
于是,为简单方便,我就直接这样写了。具体如下代码所示:
//点击在 body 里 追加一个 ul 列表 $("button").on("click",function(){ $("body").append("
- "+ "
- 我是li new 1
- "+ "
- 我是li new 2
- "+ "
- 我是li new 3
- "+ "
-
刚开始没怎么注意,不知道这样写会存在什么样的问题。 等后面测试功能的时候,我才发现了问题所在:li 元素在点击之后一点反应都没有,点了 等于 没点,样式也没变化。 后来打断点调试才发现:原来是因为 li 元素 的点击事件执行了两次。为什么会执行两次呢?
答案马上揭晓…
在页面加载完成之后,就为页面上 已经存在的 ul 列表中的 每一个 li 元素 添加了点击事件。没错吧~
此时是没有任何问题的。然后,我们 点击 “新增” 按钮,页面上就会追加 一个 新的 ul 列表。
追加完成之后呢,又会给 新增 的 ul 列表 中的 每一个 li 元素 添加点击事件。
注意:
仔细看,此时为 每一个 li 元素 添加点击事件的 父元素 ul ,居然有两个。
我的本意是 只给新增的 ul 列表 中的每一个 li 元素 添加点击事件,没想到点击按钮之后,又为 原有的 ul 列表 中的 每一个 li 元素 添加了一次点击事件。
这么算来,原有的 ul 列表 中的 每一个 li 元素 就被添加了 两次 点击事件,所以每点一次就会出现执行两次的现象。
接下来,我们点击 原有的 ul 列表中的某一个 li 元素 试试,看一下效果。
执行下一步,完成点击事件
继续往下执行,看看会发生什么?然后你会惊奇的发现:第 3个 li 元素的样式消失了…
现在去尝试点击一下,看一看 新旧 li 元素在被点击之后有什么不同之处。
分析:
没错吧,新增的 li元素点击事件不受影响,而原有的 li元素 就没有那么幸运了。 因为原有的 li元素被添加了两次点击事件,所以就会点一次执行两次,样式添加之后又被移除了。整个过程就相当于没有点击,最后看起来就好像 “点击事件失效了” 一样。解决问题
那么,如何去解决呢? 很简单,在绑定之前先解绑就可以了。 原理:在第二次为 li 元素添加点击事件之前,先将最开始为 li 元素添加的点击事件移除掉,然后重新添加一次点击事件即可。 这样做,既不影响最初时元素的点击功能,也不会在新增该元素之后,为新增元素添加点击事件时,又为原有的元素重复添加点击事件。 具体操作如下:$("ul").off("click","li").on("click","li",function(){ $(this).toggleclass('item'); });
现在我终于明白了:为什么在绑定事件之前还需要专门解绑一次事件。以前虽然经常看到这种操作,但是不明白这么做的原因,也不知道什么情况下需要先解绑再绑定事件。
结束语
一开始我百度了一下,还以为是因为冒泡的原因。当时百度的关键词是:子元素的点击事件触发两次
搜到的结果是这样的,于是就点开看了下第一个搜索结果,看看有没有什么帮助:
参考资料:js(jquery)的on绑定点击事件执行两次的解决办法
至此,问题就已经完美解决了。
附录
演示 demo 的完整代码,如下所示:
- 我是li1
- 我是li2
- 我是li3
- 我是li4
- 我是li5
- 我是li new 1
- 我是li new 2
- 我是li new 3
推荐阅读
-
on()方法绑定的事件执行两次的原因
-
JS中控制绑定事件执行顺序的详细介绍
-
动态添加的html元素绑定事件的方法
-
vue绑定事件后获取绑定事件中的this方法
-
jQuery中的bind绑定事件与文本框改变事件的临时解决方法_jquery
-
jquery中移除了live()和die(),新版事件绑定on()和off()的方法详解
-
vue element-ui 绑定@keyup事件无效的解决方法
-
AngularJS实现给动态生成的元素绑定事件的方法
-
JavaScript实现父子dom同时绑定两个点击事件,一个用捕获,一个用冒泡时执行顺序的方法
-
JavaScript call apply使用 JavaScript对象的方法绑定到DOM事件后this指向问题_javascript技巧