有以下 html 片断html
<ul> <li>11111111111</li> <li>22222222222</li> <li>33333333333</li> </ul>
要对 li 添加 click 事件。一般作法:浏览器
var list = document.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { list[i].addEventListener('click', function(e) { console.log(e.target.textContent); }) }
事件代理的作法dom
var ul = document.getElementsByTagName('ul')[0]; ul.addEventListener('click', function(e) { if (e.target.tagName === 'LI') { console.log(e.target.textContent); } })
咱们来对比一下两段注册点击事件的代码。第一段代码很常规,直接找到相关元素而后一一绑定。第二段代码看起来简洁一点,但多了一层判断。wordpress
作一个简单的 delegate 封装函数
EventTarget.prototype.delegate = function(targetDom, type, listener) { this.addEventListener(type, function(e) { if (e.target.tagName === targetDom.toUpperCase()) { listener(e); } }); } var ul = document.getElementsByTagName('ul')[0]; ul.delegate('li', 'click', function(e) { console.log(e.target.textContent); });
接下来的篇幅主要会聊到事件流,事件代理的原理和问题,event 和 EventTarget 帮你深度了解 js 的事件。ui
一般咱们把事件流分为三个阶段this
var ul = document.getElementsByTagName('ul')[0]; ul.addEventListener('click', function(e) { if (e.target.tagName === 'LI') { console.log(e.target.textContent); } }) document.body.addEventListener('click', function(e) { console.log('body', e.target.textContent); })
捕获阶段:这个就是向下去找触发事件的元素。可是必定注意若是这个时候 body 上的 click 并不会触发。若是但愿 body 上的 click 先触发,就须要给 addEventListener 第三个参数 useCapture 设置为 true (dom3 引入的,因此比较新的浏览器才有效)。spa
目标阶段:执行目标的绑定函数prototype
冒泡阶段:沿着父级元素一路向上,body 上若是也有 click 事件就会执行。event.stopPropagation() 能够用于事件处理函数中阻止冒泡行为。代理
在咱们了解了事件流以后,事件代理的原理就很好理解了,其实就是事件冒泡会触发容器 dom 的相关事件并执行监听函数。
那事件代理会带来什么问题呢?
<ul> <li>111111</li> </ul>
若是 ul 有 click ,而且在它上面为 li 作了 click 事件的代理。这个时候其实我只想触发 li 的 click。解决方法以下:
var ul = document.getElementsByTagName('ul')[0]; ul.addEventListener('click', function(e) { if (e.target === e.currentTarget) { console.log(e.target); } }); ul.delegate('li', 'click', function(e) { console.log(e.target); });
很显然 stopPropagation 并不能解决问题,这里经过比较 target 和 currentTarget 来判断当前事件触发元素(target)是不是注册事件的元素(currentTarget)便可。
stopPropagation,target,currentTarget 都是 event 的方法和属性,因此下面详细讲讲 event 对象。
currentTarget 注册事件的 dom 元素
srcElement ie6-8 的触发事件的 dom 元素,非标准
target 触发事件的 dom 元素
timeStamp 返回事件发生时的时间戳
type 事件的类型
preventDefault() 阻止默认行为。例如 a 标签默认会跳转 href 指定的连接,执行该方法能够阻止跳转的发生。
stopImmediatePropagation() 阻止冒泡行为,并当即阻止该绑定元素上的相同类型事件处理函数的执行。当即阻止的意义在于,本方法执行以前函数执行或者其余的相同类型绑定不会有问题,但一旦执行该方法后面的程序将再也不执行。例子
stopPropagation() 阻止冒泡行为。
咱们来看一下 mdn 对于 EventTarget 的定义:一个EventTarget是一个能够接受DOM事件且能绑定事件监听器的对象.最多见的EventTarget就是DOM元素对象,另外,还有一些不是DOM元素的对象也能成为EventTarget,好比document, window, XMLHttpRequest,等等.
EventTarget 有三个经常使用方法:
addEventListener(type, listener[, useCapture])
给 EventTarget 添加事件绑定,type 为事件类型(例如 ‘click’),listener 事件触发时的处理函数(默认参数 event 本文后面会讲到)。useCapture 是一个非必需的布尔值,默认为 false。若是设置为 true 即表示但愿发起捕获(关于捕获,会在后面的事件触发流程里讲到)。
removeEventListener(type, listener[, useCapture])
给 EventTarget 注销事件绑定。参数和 addEventListener 相同。须要特别注意两点。
一、若是同一个监听事件分别为“事件捕获”和“事件冒泡”注册了一次,一共两次,这两次事件须要分别移除。二者不会互相干扰。 二、若是此事件正在执行,会当即中止。
dispatchEvent
手动触发事件,举个例子(来自 mdn):
var event = new Event('build'); // Listen for the event. elem.addEventListener('build', function (e) { console.log(e.target) }, false); // Dispatch the event. elem.dispatchEvent(event);
上面这个例子就是一个简单的自定义事件,若是想深刻了解能够看张鑫旭的漫谈js自定义事件、DOM/伪DOM自定义事件