zhongxia245/blog

【20170204】JS事件委托

Closed this issue · 1 comments

时间:2016-12-03 15:20:07

问题

什么是事件委托?

一、事件冒泡

DOM2级 事件分为三个阶段(IE8以及之前不支持DOM事件流)

  • 事件捕获
  • 目标对象
  • 冒泡

W3C模型:
页面上的点击事件流程是这样的, 从不准确节点 document,HTML开始一层一层的往下找,直到找到绑定目标事件的对象, 找到之后,事件会在一层一层的冒泡,冒泡到最顶层。

//第三个参数默认是 false, 标识事件触发在冒泡阶段
先执行目标事件处理函数,在处理父容器的事件。

如果为true的话, 先执行父容器的事件,在执行目标事件

addEventListener(eventname,fn,useCaptrue)

二、事件委托

把多个有相同事件处理函数的节点,把事件绑定到他们的父节点上。减轻对内存的损耗

  • 根据事件冒泡的原理,衍生出委托事件

js事件委托,其实是使用了冒泡的原理,从点击的元素开始,递归方式的向父元素传播事件,这样做的好处是对于大量要处理的元素,不必为每个元素都绑定事件,只需要在他们的父元素上绑定一次即可,提高性能。 还有一个好处就是可以处理动态插入dom中的元素,直接绑定的方式是不行的。
就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素事件委托很好地利用了"事件冒泡"。当点击子元素,根据"事件冒泡",该子元素的父级元素捕获了该次点击事件,并触发自己的方法。

1. 优点

  • 提升性能【每个节点绑定事件,到把事件绑定到父容器上】
  • 新增的元素还会有之前的处理事件

2. 缺点

  • 不是所有的事件都能冒泡。 【focus, blur, load, unload】
  • 在管理鼠标事件的时候,需要注意,因为鼠标事件太频繁了,如果鼠标事件处理有问题,就容易造成性能瓶颈

三、demo

<ul id="parent-list">
    <li id="post-1">Post 1</li>
    <li id="post-2">Post 2</li>
    <li id="post-3">Post 3</li>
    <li id="post-4">Post 4</li>
    <li id="post-5">Post 5</li>
    <li id="post-6">Post 6</li>
</ul>

给所有 li 绑定事件

  • 写一个循环,给每一个 li 绑定上 click 事件
  • 把事件绑定在ul 上, 通过 e.target 可以获取点击节点的信息

四、Jquery的事件委托

$(document).on(‘click‘,‘li‘,function(){
    alert(‘这是一个li!!!‘);
});

用JS封装一个

/**
    
*/
function delegate(parent,selector,eventName,callback){
    
}
/**
   * 事件委托
   * @param  {[type]}   dom       [绑定事件的DOM节点]
   * @param  {[type]}   selector  [需要绑定的子节点]
   * @param  {[type]}   eventName [事件名称]
   * @param  {Function} callback  [事件处理函数]
   * @return {[type]}             []
   */
  function delegate(dom, selector, eventName, callback) {
  	if(dom){
  		dom.addEventListener(eventName,function(e){
  			e = e || window.event
  			if(e.target.tagName.toLowerCase() === selector){
  				callback && callback(e)
  			}
			})
  	}
  }

完整的事件委托方法

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>事件委托</title>
</head>

<body>
  <ul id="id-ul">
    <li>zhongxia1</li>
    <li>zhongxia2</li>
    <li>zhongxia3</li>
    <li>zhongxia4</li>
  </ul>
  <button id="id-btn">添加一个li</button>
  <script type="text/javascript">
  var ul = document.querySelector('#id-ul')
  delegate(ul, 'li', 'click', function(e) {
    console.log(e.target)
  })

  addEvent(document.querySelector('#id-btn'), 'click', function() {
  	ul.innerHTML += '<li>动态添加的Item</li>'
  })

  /**
   * 事件委托
   * @param  {[type]}   dom       [绑定事件的DOM节点]
   * @param  {[type]}   selector  [需要绑定的子节点]
   * @param  {[type]}   eventName [事件名称]
   * @param  {Function} callback  [事件处理函数]
   * @return {[type]}             []
   */
  function delegate(dom, selector, eventName, callback) {
    if (dom) {
      addEvent(dom, eventName, function(e) {
        e = e || window.event
        if (e.target.tagName.toLowerCase() === selector) {
          callback && callback(e)
        }
      },false)
    }
  }

  function addEvent(elm, evType, fn, useCapture) {
    if (elm.addEventListener) {
      elm.addEventListener(evType, fn, !!useCapture); //DOM2.0
      return true;
    } else if (elm.attachEvent) {
      var r = elm.attachEvent('on' + evType, fn); //IE5+
      return r;
    } else {
      elm['on' + evType] = fn; //DOM 0
    }
  }
  </script>
</body>

</html>
cji03 commented

delegate方法中,if (e.target.tagName.toLowerCase() === 'li')应该等于selector吧,你这写死了。然后e.target也应该hack一下。

addEventuseCapture应该是Ture或者False吧。