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

Write By Monkeyfly

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

前提

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

具体是这样的:

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

问题来了:

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

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

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

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

在这里插入图片描述

出现问题的原因:

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

情景还原

  • 首先,页面 HTML 当中本来就存在一个 静态的ul列表【如下图所示】
<body>
	<ul>
		<li>我是li1</li>
		<li>我是li2</li>
		<li>我是li3</li>
		<li>我是li4</li>
		<li>我是li5</li>
	</ul>
	<button>增加一些li元素</button>
</body>

在这里插入图片描述

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

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

//点击在 body 里  追加一个 ul 列表
$("button").on("click",function(){
	$("body").append("<ul>"+
						"<li onclick='change(this)'>我是li new 1</li>"+
						"<li onclick='change(this)'>我是li new 2</li>"+
						"<li onclick='change(this)'>我是li new 3</li>"+
					"</ul>");
});

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

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

//点击在 body 里  追加一个 ul 列表
$("button").on("click",function(){
	$("body").append("<ul>"+
						"<li>我是li new 1</li>"+
						"<li>我是li new 2</li>"+
						"<li>我是li new 3</li>"+
					"</ul>");
	//然后也需要为新增的 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(),发现并没有效果。
  • 后来经过自己打断点调试,才定位到问题所在。
  • 我们都遇到了同样的问题,但我的问题确实是事件绑定的原因。而他问题的元凶则是事件冒泡

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

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

附录

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

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>点击子元素事件执行两次</title>
	<!--jquery引用自己的就行-->
	<script src="jquery-3.2.1.min.js"></script>
	<style>
		li{
			width: 100px;
			height: 30px;
			line-height: 30px;
			background:#fff;
			color:#333;
			margin-top:10px;
			text-align: center;
			cursor:pointer;
			border:1px solid #ddd;
		}
		li::selection{
			background-color: transparent;
		}
		li.item{
			background:#f65e62;
			color:#fff;
			border:none;
		}
	</style>
</head>
<body>
	<ul>
		<li>我是li1</li>
		<li>我是li2</li>
		<li>我是li3</li>
		<li>我是li4</li>
		<li>我是li5</li>
	</ul>
	<button>增加一些li元素</button>
	<script>
		$("ul").on("click","li",function(){
			$(this).toggleClass('item');
		})
		/*$("li").on('click', function(event) {
			$(this).toggleClass('item');
		});*/
		$("button").on("click",function(){
			$("body").append("<ul><li>我是li new 1</li><li>我是li new 2</li><li>我是li new 3</li></ul>");
			$("ul").off("click","li").on("click","li",function(){
				$(this).toggleClass('item');
			});
			// $("ul").on("click","li",function(){
			// 	$(this).toggleClass('item');
			// });
		});
	</script>
</body>
</html>
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页