js事件传播机制之冒泡事件详解

1 首先,了解一下事件

事件是用户或浏览器执行的某种动作,例如click:鼠标单击事件、dblclick:鼠标双击事件、focus:获取焦点事件等。

事件是javascript与dom交互的桥梁。

2 浏览器事件传播的过程--事件流

事件流是事件从页面接受并传播的过程,主要分三个阶段:事件捕获阶段、目标事件阶段、事件冒泡阶段。

事件捕获:事件的传播从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子。(最外层到触发元素)

事件冒泡:事件的传播从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。(触发元素到最外层)

目标事件:事件传到目标元素所产生的行为。(例如绑定在button的click事件)

ps:事件的默认行为是指dom元素默认执行的动作,比如点击a标签会发生页面跳转等等。

IE提出的是冒泡流,而网景提出的是捕获流,后来在W3C组织的统一之下,JS支持了冒泡流和捕获流,但是目前低版本的IE浏览器还是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),所以为了能够兼容更多的浏览器,建议大家使用冒泡流。

看一段代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script>
		<style type="text/css">
			div {
				width: 300px;
				height: 150px;
				background: #ddd;
			}
			body  {
				background: #ccc;
			}
		</style>
	</head>
	<body onclick="bodyClick()">
		<div onclick="divClick()">
			<button onclick="btnClick()">点击我</button>
		</div>
	</body>
	<script type="text/javascript">
		function bodyClick() {
			alert("body click");
		}
		function divClick() {
			alert("div click");
		}
		function btnClick() {
			alert("btn click");
		}
	</script>
</html>

此处大家可能会疑惑,点击button,为什么浏览器会依次弹出btn click、div click、body click三个弹出窗口。事件从button依次执行到body,这是一个事件冒泡流。

3 事件冒泡流

事件冒泡:事件的传播从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。(触发元素到最外层)

例如,div里放一个button,在button和div上同时绑定click事件,点击button,先执行button的click事件,再执行div的click事件。

4 为什么要阻止冒泡?

开发中有这样需求:点击button弹出单选框,然后点击空白处,关闭单选框。此时,如果不阻止button的冒泡事件,则会出现下拉列表打不开的bug。

示例代码1 (不阻止冒泡 点击按钮列表不打开)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script>
		<style type="text/css">
			*{
				list-style: none;
			}
			div {
				width: 100%;
				height: 150px;
				background: #ddd;
			}
			ul {
				padding: 0;
				width: 30%;
				border: 1px solid #666;
				display: none;
			}
		</style>
	</head>
	<body>
		<div onclick="closeList()">
			<button onclick="openList()">点击我 显示列表</button>
			<ul>
				<li>item 1</li>
				<li>item 2</li>
				<li>item 3</li>
			</ul>
		</div>
	</body>
	<script type="text/javascript">
		function openList() {
			$("ul").css("display","block");
		}
		function closeList() {
			$("ul").css("display","none");
		}
	</script>
</html>

示例代码2 (阻止冒泡 点击按钮打开列表 点击空白隐藏列表)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script>
		<style type="text/css">
			*{
				list-style: none;
			}
			div {
				width: 100%;
				height: 150px;
				background: #ddd;
			}
			ul {
				padding: 0;
				width: 30%;
				border: 1px solid #666;
				display: none;
			}
		</style>
	</head>
	<body>
		<div onclick="closeList()">
			<button onclick="openList()">点击我 显示列表</button>
			<ul>
				<li>item 1</li>
				<li>item 2</li>
				<li>item 3</li>
			</ul>
		</div>
	</body>
	<script type="text/javascript">
		function openList() {
			$("ul").css("display","block");
			event.stopPropagation();
		}
		function closeList() {
			$("ul").css("display","none");
		}
	</script>
</html>

所以阻止冒泡的目的就是让元素只执行绑定在它上面的方法,而不会让事件继续传播下去。

5 关于阻止冒泡的兼容性

示例代码2在google、搜狗等浏览器可以正常执行,但是在火狐浏览器列表还是不会打开。

是因为浏览器内核的差异,chrome内核浏览器通过

event.stopPropagation();

阻止冒泡;

火狐浏览器则需要在元素的click事件里传入event参数后,

arguments.callee.caller.arguments[0]

取得可执行stopPropagation方法的参数后通过stopPropagation()阻止冒泡

示例代码-火狐浏览器阻止冒泡

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script>
		<style type="text/css">
			*{
				list-style: none;
			}
			div {
				width: 100%;
				height: 150px;
				background: #ddd;
			}
			ul {
				padding: 0;
				width: 30%;
				border: 1px solid #666;
				display: none;
			}
		</style>
	</head>
	<body>
		<div onclick="closeList()">
			<button onclick="openList(event)">点击我 显示列表</button>
			<ul>
				<li>item 1</li>
				<li>item 2</li>
				<li>item 3</li>
			</ul>
		</div>
	</body>
	<script type="text/javascript">
		function openList(e) {
			$("ul").css("display","block");
			var e = arguments.callee.caller.arguments[0];
			e.stopPropagation();
		}
		function closeList() {
			$("ul").css("display","none");
		}
	</script>
</html>

IE浏览器则是使用

window.event.cancelBubble = true;

阻止冒泡

6 js封装兼容全浏览器阻止冒泡的方法

function stopPropagation(e) {
    // 火狐浏览器必须在元素事件里传入event参数
    var e = window.event || arguments.callee.caller.arguments[0];
    if(e && e.stopPropagation) {
        e.stopPropagation();
    }else {
        window.event.cancelBubble = true;
    }
}

注意:火狐浏览器必须在元素绑定的事件里传入event参数,类似

<button onclick="openList(event)">点击我 显示列表</button>

7 angular.js阻止冒泡事件

<button ng-click="openList($event)"></button>

元素的ng事件里传入,$event参数,然后在controller里的通过e.stopPropagation(),经测试此方法兼容大多主流浏览器。

$scope.openList = function(e) {
	e.stopPropagation();
}








猜你喜欢

转载自blog.csdn.net/wq18512847606/article/details/80757781