术语定义:
1.事件流
描述的是从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。
2.事件
就是用户或浏览器自身执行的某种动作。诸如click(点击)、load(加载)、mouseover(鼠标悬停)。
3.事件处理程序
响应某个事件的函数就叫事件处理程序(或事件侦听器)。
事件流
描述的是从页面中接受事件的顺序,但有意思的是,
IE和Netscape开发团队居然提出了两个截然相反的事件流概念:
IE的事件流是事件冒泡流;
而Netscape的事件流是事件捕获流;
IE的事件流是冒泡, 从里面往上面冒, netscape是从外部元素往内部元素捕获;
DOM2级的事件
规定了事件流包含三个阶段包括: 1:事件捕获, 2:处于目标阶段, 3:事件冒泡阶段(IE8以及更早版本不支持DOM事件流); 无论在DOM0还是DOM2还是DOM3中都会在事件函数中传入事件对象;
捕获阶段(event capturing):在事件冒泡的模型中,捕获阶段不会响应任何事件;
通俗的理解就是,当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。
目标阶段:目标阶段就是指事件响应到触发事件的最底层元素上;
冒泡阶段(dubbed bubbling):冒泡阶段就是事件的触发响应会从最底层目标一层层地向外到最外层(根节点),事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层;
无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播
,它就像一跟引线,只有通过引线才能将绑在引线上的×××(事件监听器)引爆,试想一下,如果引线不导火了,那×××就只有一响了!
DOM2级事件'定义了两个方法,
用于处理指定和删除事件处理程序的操作:addEventListener()、removeEventListener()
说到事件冒泡与捕获就不得不提一下两个用于事件绑定的方法addEventListener、attachEvent。当然还有其它的事件绑定的方式这里不做介绍。
addEventListener(event, listener, useCapture)
参数定义:
event---(事件名称,如click,不带on),
listener---事件监听函数,
useCapture---是否采用事件捕获进行事件捕捉(默认为false,即采用事件冒泡方式)
addEventListener
在 IE11、Chrome 、Firefox、Safari等浏览器都得到支持。
attachEvent(event,listener)
参数定义:
event---(事件名称,如onclick,带on)
listener---事件监听函数。
attachEvent
主要用于IE浏览器,并且仅在IE10及以下才支持,IE11已经废了这个方法了(微软还是挺识趣的,慢慢向标准靠拢)。
下面就用上面这两个方法通过例子来解释一下事件捕获与事件冒泡的具体表现行为差异,
事件冒泡
例1:
js事件机制
父元素
子元素
通过"addEventListener"方法,采用事件冒泡方式给dom元素注册click事件,点击子元素会发生什么呢?
如果你对事件冒泡有一定了解的话那你肯定知道上面的代码会输出的顺序,没错,如下图所示:
事件触发顺序是由内到外的,这就是事件冒泡,虽然只点击子元素,但是它的父元素也会触发相应的事件,其实这是合理的,因为子元素在父元素里面,点击子元素也就相当于变相的点击了父元素,这样理解对吧?
这里有同学可能要问了,如果点击子元素不想触发父元素的事件怎么办?
肯定可以的,那就是停止事件传播---event.stopPropagation();
修改例1的代码,在子元素的监听函数中加入停止事件传播的操作,例2:
child.addEventListener("click",function(e){ console.log("click-child"); e.stopPropagation(); },false);
在点击子元素的时候就只弹出了子元素那条信息,父元素的事件没有触发,因为事件已经停止传播了,冒泡阶段也就停止了。
事件冒泡差不多就讲述完了,别急,捕获还没说呢!
事件捕获
例3,修改例子1中的代码,给parent元素注册一个捕获事件,如下:
var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.body.addEventListener("click",function(e){ console.log("click-body"); },false); parent.addEventListener("click",function(e){ console.log("click-parent---事件传播"); },false); //新增事件捕获事件代码 parent.addEventListener("click",function(e){ console.log("click-parent--事件捕获"); },true); child.addEventListener("click",function(e){ console.log("click-child"); },false);
如果你看明白了我前面说的那些,你就知道这个栗子的输出顺序了:
父元素通过事件捕获的方式注册了click事件,所以在事件捕获阶段就会触发,然后到了目标阶段,即事件源,之后进行事件传播,parent同时也用冒泡方式注册了click事件,所以这里会触发冒泡事件,最后到根节点。这就是整个事件流程。
敲黑板了啊:《javascript 事件取消和阻止冒泡》
---取消默认操作
w3c 的方法是 e.preventDefault(),IE 则是使用 e.returnValue = false;
在支持 addEventListener() 的浏览器中,也能通过调用时间对象的 preventDefault() 方法取消时间的默认操作。不过,在 IE9 之前的 IE 中,可以通过设置事件对象的 returnValue 属性为 false 来达到同样的效果。下面的代码假设一个事件处理程序,它使用全部的三种取消技术:
function cancelHandler(event){ var event = event || window.event; //用于IE if(event.preventDefault) event.preventDefault(); //标准技术 if(event.returnValue) event.returnValue = false; //IE return false; //用于处理使用对象属性注册的处理程序 }
当前的 DOM 事件模型草案定义了 Event 对象属性 defaultPrevented。
return false
javascript 的 return false 只会阻止默认行为,而是用 jQuery 的话则既阻止默认行为又防止对象冒泡。
下面这个使用原生 JS,只会阻止默认行为,不会停止冒泡
var a = document.getElementById("testB");
a.onclick = function(){
return false;
};
---阻止冒泡
w3c 的方法是 e.stopPropagation(),IE 则是使用 e.cancelBubble = true
在支持 addEventListener() 的浏览器中,可以调用事件对象的一个 stopPropagation() 方法已阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation() 方法可以在事件传播期间的任何时间调用,它能工作在捕获阶段、事件目标本身中和冒泡阶段。
IE9 之前的IE不支持 stopPropagation() 方法。相反,IE事件对象有一个 cancleBubble 属性,设置这个属性为 true 能阻止事件进一步传播。( IE8 及之前版本不支持事件传播的捕获阶段,所以冒泡是唯一待取消的事件传播。)
当前的 DOM 事件规范草案在 Event 对象上定义了另一个方法,命名为stopImmediatePropagation()。类似 stopPropagation(),这个方法组织了任何其他对象的事件传播,但也阻止了在相同对象上注册的任何其他事件处理程序的调用。
- test
阻止冒泡
function stopHandler(event) window.event?window.event.cancelBubble=true:event.stopPropagation(); }
注意:
关于DOM0、DOM2、DOM3级事件模型请参考我的另一篇博客:
《理解:javascript中DOM0,DOM2,DOM3级事件模型》
参考文章:
北方吹雪的博客:http://www.cnblogs.com/bfgis/p/5460191.html
开源中国2015的博客:http://wiki.jikexueyuan.com/project/brief-talk-js/event-cancellation-and-prevent-bubbles.html