博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
on()方法绑定的事件执行两次的原因
阅读量:4196 次
发布时间:2019-05-26

本文共 2982 字,大约阅读时间需要 9 分钟。

Write By Monkeyfly

以下内容均为原创,如需转载请注明出处。

前提

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

具体是这样的:

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

问题来了:

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

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

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

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

在这里插入图片描述

出现问题的原因:

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

情景还原

  • 首先,页面 HTML 当中本来就存在一个 静态的ul列表【如下图所示】
	
  • 我是li1
  • 我是li2
  • 我是li3
  • 我是li4
  • 我是li5

在这里插入图片描述

  • 然后,在页面加载完成之后,需要为 ul 中的每一个 li 元素添加点击事件。来实现点击选中,再次点击取消的效果。
  • 点击 新增按钮 之后,页面中又追加了一个 新的 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'); });});

-在这里插入图片描述

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

为什么会执行两次呢?

答案马上揭晓…

  • 在页面加载完成之后,就为页面上 已经存在的 ul 列表中的 每一个 li 元素 添加了点击事件。没错吧~

    在这里插入图片描述

  • 此时是没有任何问题的。然后,我们 点击 “新增” 按钮,页面上就会追加 一个 新的 ul 列表

    在这里插入图片描述

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

注意:

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

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

  • 这么算来,原有的 ul 列表 中的 每一个 li 元素 就被添加了 两次 点击事件,所以每点一次就会出现执行两次的现象

    在这里插入图片描述

  • 接下来,我们点击 原有的 ul 列表中的某一个 li 元素 试试,看一下效果。

    在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

  • 在这里插入图片描述

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

在这里插入图片描述

分析:

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

解决问题

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

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

结束语

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

搜到的结果是这样的,于是就点开看了下第一个搜索结果,看看有没有什么帮助:

在这里插入图片描述

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

参考资料:

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

附录

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

	
点击子元素事件执行两次
  • 我是li1
  • 我是li2
  • 我是li3
  • 我是li4
  • 我是li5
你可能感兴趣的文章
关于在openstack的环境变量.bashrc自定自己简化命令
查看>>
Openstack Heat Project介绍(转)
查看>>
How to Perform an Upgrade from Icehouse to Juno(ice升级到juno)
查看>>
高扩展性网站的50条原则(转)-思维导图
查看>>
解决openstack novnc一段时间后自动挂断登录不上问题,novncproxy dead but pid file exists
查看>>
构建OpenStack的云基础架构:ManageIQ(转)
查看>>
云管理软件 ManageIQ(转)
查看>>
CentOS 7.0,启用iptables防火墙(转)
查看>>
DISCUZ浅析之COOKIE篇
查看>>
实战DDD(Domain-Driven Design领域驱动设计:Evans DDD)
查看>>
SSH中各个框架的作用以及Spring AOP,IOC,DI详解
查看>>
openstack juno 配置vmware(vcenter、vsphere)
查看>>
远程debug调试(eclipse)之openstack windows
查看>>
PAAS平台对比:OpenShift VS CloudFoundry【51CTO调研报告】
查看>>
JAX-RS(java restful实现讲解)(转)
查看>>
Spring MVC与JAX-RS比较与分析
查看>>
openstack官方docker介绍
查看>>
头痛与早餐
查看>>
[转]在ASP.NET 2.0中操作数据::创建一个数据访问层
查看>>
Linux命令之chmod详解
查看>>