JS事件流机制
一个完整的JS事件流是从window开始,最后回到window的一个过程,事件流被分为三个阶段:
(1~5)捕获过程、(5~6)目标过程、(6~10)冒泡过程。
通过addEventListener可以监听冒泡阶段的事件,如果第三个参数指定为true,则监听的是捕获阶段的事件。低版本IE(6,7,8)不支持捕获阶段的事件监听,只能用attachEven来监听冒泡阶段事件。这不意味着低版本IE没有事件捕获,其实有,只不过没办法去监听而已
attachEvent和addEventListener的对比
- 两者都可以为一个元素添加多个事件监听,不会出现覆盖问题
- 前者回调函数中this指向window,后者回调函数中this指向当前元素
- 前者监听的是事件冒泡,而后者通过第三个参数可以指定为冒泡(默认false)或者捕获(true)
- 对于阻止事件冒泡,前者调用e.cancelBubble=true,而后者调用e.stopPropagation()
- 事件名不一样:前者“onclick”,而后者“click”
- 前者支持IE10及以下(IE11以及以上使用的话,会提示没有这个方法),而后者IE9及以上和非IE浏览器
阻止默认行为
标准写法:
function cancelHandler(event){ event=event||window.event;//兼容IE //取消事件相关的默认行为 if(event.preventDefault) //标准技术 event.preventDefault(); if(event.returnValue) //兼容IE9之前的IE event.returnValue=false; return false; //用于处理使用对象属性注册的处理程序 }
因为IE6,7,8回调函数的没有传入参数,而是要通过window.event来获取事件对象;而且ff中没有全局的window.event事件对象,所以兼容的写法就是 event || window.event
dom0级事件(onclick=xxx)中,使用e.returnValue = false【IE9以上不支持】 、e.preventDefault()【IE6,7,8不支持】或者return false【主要用在dom0级事件中,如onclick=xx】都有效
dom2级事件(addEventListener)中,使用return false无效
attachEvent中使用e.preventDefault()无效
ps:阻止a标签的默认行为。
事件委托
利用事件冒泡,把对多个子元素的事件监听,转换为对一个父元素的事件监听,可以提升效率(减少内存对象)。其实利用捕获或者冒泡实现都可以,只不过主流浏览器都支持事件冒泡,所以从兼容性的角度来还是选用冒泡。
<ul id="color-list"> <li>red</li> <li>yellow</li> <li>blue</li> <li>green</li> <li>black</li> <li>white</li> </ul> (function(){ var color_list = document.getElementById('color-list'); color_list.addEventListener('click',showColor,false); function showColor(e){ var x = e.target; if(x.nodeName.toLowerCase() === 'li'){ alert('The color is ' + x.innerHTML); } } })();
以上使用委托除了节省了内存外,还有另外一个好处,当动态添加了一个子元素,不需要为这个子元素绑定事件,更加便捷。
事件目标
evt中对于事件对象有三个属性:currentTarget、target和srcElement
currentTarget:代表当前节点,一般与this指向相同。(attachEvent中this指向window)
target和srcElement:实际发生事件的dom对象。ff不支持srcElement,所以兼容的写法是: var target = e.target || e.srcElement
事件其他属性
bubbles:代表当前事件是否会冒泡,除了focus、blur和scroll三种事件不会冒泡之外,其他类型的事件改属性都为true
cancelBubble:默认为false,设置为true时,用于阻止冒泡,对捕获阶段无效,全浏览器支持,但不是标准写法,应尽量使用stopPropagation
stopPropagation() :阻止事件在事件流(先捕获后冒泡)中的传播。一种情况例外,如果在A上捕获事件,里面执行stopPropagation,则A的冒泡回调也会执行:
<div id="a"> 123 <div id="b"> 456 <div id="c"> 789 </div> </div> </div> <script> var a = document.querySelector("#a"); var b = document.querySelector("#b"); var c = document.querySelector("#c"); a.addEventListener('click',(e)=>{ console.log("a") },true) b.addEventListener('click',(e)=>{ console.log("b") e.stopPropagation() },true) c.addEventListener('click',(e)=>{ console.log("c") },true) a.addEventListener('click',(e)=>{ console.log("2 a") }) b.addEventListener('click',(e)=>{ console.log("2 b") }) c.addEventListener('click',(e)=>{ console.log("2 c") },true) </script> 点击456,输出 a b 2b。
stopImmediatePropagation():在阻止事件传播的基础上,同时阻止当前元素上对同一事件的其他回调函数的执行
总结低版本IE与其他浏览器的事件差异
- 获取事件对象的方式不同
- 取消默认行为的方式不同
- 可以检测控制的事件流不同