由event target引发的关于事件流的一连串思考(二)

462 阅读4分钟
阻止事件冒泡

W3C的方法是ev.stopPropagation(),IE则是使用ev.cancelBubble = true。

先不谈IE的私有方法,首先讨论一个问题:ev.stopPropagation()真的是阻止事件冒泡吗?

实际上,这个问题需要分两种情况来讨论:

  1. DOM0的阻止冒泡事件 还是同心圆的例子,代码如下。 JavaScript:
var div = document.querySelector("div");
var ul = document.querySelector("ul");
var li = document.querySelector("li");
div.onclick = function(ev){
	console.log("div");
}
ul.onclick = function(ev){
	console.log("ul");
}
li.onclick = function(ev){
	console.log("li");
	ev.stopPropagation();
}

此时输出结果如下:

DOM0阻止事件
可以看到阻止事件冒泡成功了,证明DOM0确实是阻止事件冒泡。

  1. DOM2的阻止冒泡事件 还是同心圆的例子,代码如下:
var div = document.querySelector("div");
var ul = document.querySelector("ul");
var li = document.querySelector("li");
div.addEventListener('click',function(){
	console.log("div 捕获");
},true);
ul.addEventListener('click',function(ev){
	console.log("ul 捕获");
},true);
li.addEventListener('click',function(ev){
	console.log("li 捕获");
},true);
div.addEventListener('click',function(ev){
	console.log("div 冒泡");
});
ul.addEventListener('click',function(ev){
	console.log("ul 冒泡");
});
li.addEventListener('click',function(ev){
	console.log("li 冒泡");
});

此时点击内层圆,输出结果如下:

没有阻止事件冒泡的情况
我们将

ul.addEventListener('click',function(ev){
	console.log("ul 冒泡");
});

改为

ul.addEventListener('click',function(ev){
	console.log("ul 冒泡");
	ev.stopPropagation();
});

此时点击内层圆,输出结果如下:

阻止ul的事件冒泡
可以看到本来会冒泡到div上的点击事件被阻止了,此时跟DOM0的阻止事件冒泡是一致的。 但是如果我们改为阻止在ul的捕获事件上阻止事件冒泡的话,事件捕获还会进行吗?还是只会阻止冒泡?我们来测试一下,将

ul.addEventListener('click',function(ev){
	console.log("ul 捕获");
},true);

改为

ul.addEventListener('click',function(ev){
	console.log("ul 捕获");
	ev.stopPropagation();
},true);

此时的输出结果如下:

阻止ul的事件捕获
可以看到,不但冒泡的整个阶段被阻止了,而且li的事件捕获也被阻止了。

所以总结一下这两点,ev.stopPropagation()不止可以阻止事件冒泡,如果在捕获阶段使用还可以阻止事件捕获。

那么我们再考虑一种特殊情况:如果ev.stopPropagation()在target阶段执行会是什么情况,代码如下。 JavaScript:

div.addEventListener('click',function(){
	console.log("div 捕获");
},true);
ul.addEventListener('click',function(ev){
	console.log("ul 捕获");
},true);
li.addEventListener('click',function(ev){
	console.log("li 捕获");
	ev.stopPropagation();
},true);
div.addEventListener('click',function(ev){
	console.log("div 冒泡");
});
ul.addEventListener('click',function(ev){
	console.log("ul 冒泡");
});
li.addEventListener('click',function(ev){
	console.log("li 冒泡");
});

我们在target上阻止了事件监听,这样整个事件冒泡应该是被阻止的,我们看输出结果:

在target上阻止了事件监听
target的冒泡仍然执行了,其实原因很简单,我们在上一章已经提到了,就不多做赘述了。

阻止事件冒泡的兼容写法:

if(event.stopPropagation){
	event.stopPropagation();
}else{
	event.cancelBubble = true;
}
阻止默认事件

这个没什么好讲的,用于阻止浏览器默认的事件,比如提交按钮的点击默认提交,a标签的点击跳转等。不过之前遇到过一个问题,在jsp里无法阻止提交按钮的提交事件,最后还是换成了div然后使用form.submit()提交才通过。 兼容写法如下:

if(event.preventDefault){
	// W3C的阻止默认事件
	event.preventDefault();
}else{
	// IE私有的阻止默认事件
	event.returnValue = false;
}
事件流的常见应用:事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

阻止事件冒泡应用

有一个很应景的例子,页面有一个弹出框,可拖拽,弹出框内有一个input框,这个框内输入的文字要可以选中。 我们先来实现页面结构和拖拽功能,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		*{
			margin: 0;
			padding: 0;
		}
		div{
			position: absolute;
			width: 300px;
			height: 100px;
			background-color: #2578b5;
			box-sizing: border-box;
			padding: 30px;
		}
		input{
			width: 100%;
			line-height: 1.6em;
			font-size: 16px;
		}
	</style>
</head>
<body>
	<div>
		<input type="text" value="用于复制的文本">
	</div>
	<script>
		var div = document.querySelector('div');
		var input = document.querySelector('input');
		div.onmousedown = function(ev){
			var mouseLeftDiv = ev.clientX - div.offsetLeft;
			var mouseTopDiv = ev.clientY - div.offsetTop;
			document.onmousemove = function(ev){
				var divLeftBody = ev.clientX - mouseLeftDiv;
				var divTopBody = ev.clientY - mouseTopDiv;
				var docWidth = document.documentElement.clientWidth;
				var docHeight = document.documentElement.clientHeight;
				if(divLeftBody <= 0){
					divLeftBody = 0;
				}else if(divLeftBody >= docWidth - div.offsetWidth){
					divLeftBody = docWidth - div.offsetWidth;
				};
				if(divTopBody <= 0){
					divTopBody = 0;
				}else if(divTopBody >= docHeight - div.offsetHeight){
					divTopBody = docHeight - div.offsetHeight;
				};
				div.style.left = divLeftBody + 'px';
				div.style.top = divTopBody + 'px';
			}
			document.onmouseup = function(){
				document.onmousemove = null;
			}
		}
	</script>
</body>
</html>

此时页面如下:

可拖拽页面
拖拽功能实现了,但是我们发现想要复制文本的时候,没有复制到,反而也触发了拖拽功能,这就很尴尬了。

这时我们的阻止事件冒泡就起作用了,我们在input框上阻止mousedown事件冒泡到外层,代码如下: JavaScript:

input.onmousedown = function(ev){
	ev.stopPropagation();
}

大功告成,此时既可以复制文本,外层又可以拖拽。

参考资料: http://www.cnblogs.com/liugang-vip/p/5616484.html http://www.cnblogs.com/libin-1/p/6368323.html