DOM事件流与事件委托

524 阅读2分钟

事件流

  事件流的三个阶段

  1. 捕获阶段:事件从Document节点自上而下向目标节点传播的阶段
  2. 目标阶段:真正的目标节点正在处理事件的阶段
  3. 冒泡阶段:事件从目标节点自下而上向Document节点传播的阶段

通过一个案例来观察一下捕获和冒泡的行为。(addEventListener的第三个参数是一个布尔值,可以决定注册的时间是捕获事件(true)还是冒泡事件(false))

index.html

<div class="wrap">
  <div class="outer">
    <div class="inner"></div>
  </div>
</div>

index.css

.wrap {
  width: 300px;
  height: 300px;
  backgroundColor: #424242;
}

.wrap .outer {
  width: 200px;
  height: 200px;
  backgroundColor: #989898;
}

.wrap .outer .inner {
  width: 100px;
  height: 100px;
  backgroundColor: #ededed;
}

index.js

var wrap = document.getElementsByClassName('wrap')[0],
    outer = wrap.getElementsByClassName('outer')[0],
    inner = outer.getElementsByClassName('inner')[0];

wrap.addEventListener('click', function () {
  console.log('捕获: wrap')
}, true);

outer.addEventListener('click', function () {
  console.log('捕获: outer')
}, true);
inner.addEventListener('click', function () {
  console.log('事件源: inner')
}, true)

wrap.addEventListener('click', function () {
   console.log('冒泡: wrap')
}, false);

outer.addEventListener('click', function () {
  console.log('冒泡: outer')}, false);

inner.addEventListener('click', function () {
  console.log('事件源: inner')
}, false);

当我们点击inner盒子时,控制台打印出右上图的内容,我们可以看出事件捕获先执行,然后到目标阶段也就是作用到事件源上时,正常执行函数,然后是事件冒泡。

注意:

  • onclick、attachEvent(ie) 只能得到冒泡阶段。
  • onblur、onfocus、onmouseenter、onmouseleave这些事件是没有冒泡的

事件源对象

一个元素触发某一个事件,浏览器就回吧这个事件触发以后的详细信息包装成一个对象传递到事件处理函数的参数中。IE是保存在window.event中。这个对象中的target、srcElement就是事件源对象。

Firefox浏览器只有target属性,IE浏览器只有srcElement属性,chrome浏览器两个都有,所以我们如果要考虑兼容性问题的话我们可以做以下的处理。

node.addEventListener('cilick', function (ev) {
  var e = ev || window.event,
      tar = e.target || e.srcElement;
}, false)

事件委托

事件委托,又称事件代理。就是把原本需要绑定在子元素的响应事件委托给父元素,让父元素来当事件的监听者,原理就是DOM元素的事件冒泡。

事件委托有什么好处呢?

  • 可以大量的节省内存,减少事件的注册
  • 可以实现当新增子元素时无需再对其绑定事件(动态绑定)

案例演示:

index.html

<ul>
  <li>1</li>
  <li>2</li> 
  <li>3</li>
</ul>
<button>add</button>

index.js

var oList = document.getElementsByTagName('ul')[0],
    oLi = oList.getElementsByTagName('li'),
    oBtn = document.getElementsByTagName('button')[0];

oList.onclick = function (ev) {
  var e = ev || window.event,
      tar = e.target || e.srcElement,
      tagName = tar.tagName.toLowerCase(); // -> 这里获取的是大写的,所以我们为了方便转了小写

  if (tagName === 'li') {
    console.log(tar.innerText); // 这里打印li内的文本内容
  }
}
// 这里动态的为ul里添加li,测试新增的li是否可以打印出他自身的文本内容
oBtn.onclick = function () {
  var li = document.createElement('li');
  li.innerText = oLi.length + 1;
  oList.appendChild(li);
}