事件委托(事件代理)

  • 概念:

通俗的说是这个事件须要绑定在我身上的,绑定在别人身上,也能达到一样的效果,也就是绑定在别人身上,我也能监听到。javascript

  • 产生的缘由:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul id="ul-test">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
</ul>
</body>
<script type="text/javascript">
  var oUl=document.getElementById("ul-test");
  var oLi=oUl.getElementsByTagName("li");
  for(var i=0,len=oLi.length;i<len;i++){
    oLi[i].addEventListener("click",function(){
      alert(this.innerHTML)
    })
  }
</script>
</html>
复制代码

问题在于:
1.for循环,循环的是li,10个li就循环10次,绑定10次事件,1000个就循环了1000次,绑定1000次事件!
2.若是li不是原本就在页面上的,是将来元素,是页面加载了,再经过js动态加载进来了,上面的写法是无效的,点击li是没有反应的!html

应该怎么解决以上问题?java

  • 解决的办法就是经过事件委托:

var oUl=document.getElementById("ul-test");
oUl.addEventListener("click",function(ev){
  console.log(ev)
  var ev=ev||window.event;
  var target=ev.target||ev.srcElement;
  //若是点击的最底层是li元素
  if(target.tagName.toLowerCase()==='li'){
    alert(target.innerHTML)
  }
})复制代码

  •   原理:

  1. 事件的传播有三个阶段:

当一个事件发生之后,它会在不一样的DOM节点之间传播(propagation)。这种传播分红三个阶段:浏览器

  • 第一阶段:从window对象传导到目标节点,称为“捕获阶段”(capture phase)。bash

  • 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。函数

  • 第三阶段:从目标节点传导回window对象,称为“冒泡阶段”(bubbling phase)。ui

这种三阶段的传播模型,会使得一个事件在多个节点上触发。好比,假设点击<div>之中嵌套一个<p>节点。
this

<div>
  <p>Click Me</p>
</div>复制代码

若是对这两个节点的click事件都设定监听函数,则click事件会被触发四次。
spa

var phases = {
  1: 'capture',
  2: 'target',
  3: 'bubble'
};

var div = document.querySelector('div');
var p = document.querySelector('p');

div.addEventListener('click', callback, true);
p.addEventListener('click', callback, true);
div.addEventListener('click', callback, false);
p.addEventListener('click', callback, false);

function callback(event) {
  var tag = event.currentTarget.tagName;
  var phase = phases[event.eventPhase];
  console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
}

// 点击之后的结果
// Tag: 'DIV'. EventPhase: 'capture'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'DIV'. EventPhase: 'bubble'复制代码
  1. 捕获阶段:事件从<div><p>传播时,触发<div>click事件;
  2. 目标阶段:事件从<div>到达<p>时,触发<p>click事件;
  3. 目标阶段:事件离开<p>时,触发<p>click事件;
  4. 冒泡阶段:事件从<p>传回<div>时,再次触发<div>click事件。

注意,用户点击网页的时候,浏览器老是假定click事件的目标节点,就是点击位置的嵌套最深的那个节点(嵌套在<div>节点的<p>节点)。因此,<p>节点的捕获阶段和冒泡阶段,都会显示为target阶段。
代理

事件传播的最上层对象是window,接着依次是documenthtmldocument.documentElement)和bodydocument.body)。也就是说,若是<body>元素中有一个<div>元素,点击该元素。事件的传播顺序,在捕获阶段依次为windowdocumenthtmlbodydiv,在冒泡阶段依次为divbodyhtmldocumentwindow

定义:

因为事件会在冒泡阶段传向父节点,所以能够把子节点的监听函数绑定在父节点上,用父节点的监听函数统一处理多个子节点的事件,这就是事件代理。

var ul = document.querySelector('ul');

ul.addEventListener('click', function(event) {
  if (event.target.tagName.toLowerCase() === 'li') {
    // some code
  }
});复制代码

上面这个函数事件监听绑在ul身上,可是监听到点击li的事件,而后处理li点击后的回调。

就算li是动态添加进来的依然可以监听到。

总结:

因为一个事件在传播的过程当中有三个阶段,由最外层的父节点一层层传递到最里面的子节点,先是捕获阶段,到目标阶段,再到冒泡阶段。若是咱们想监听到动态添加的子节点的事件,咱们能够把事件绑在父节点上,利用event.target,筛选到子节点,处理事件相应的回调!!!

相关文章
相关标签/搜索