5-12 委托实现
本节编程练习不计算学习进度,请电脑登录imooc.com操作

委托实现

在执行事件的时候,jQuery 会根据事件绑定的时候处理来执行事件的逐渐触发,我们观察一组代码:

div.on('mousedown', 'li', function(e) {
  show('委托到li触发')
})
div.on('mousedown', 'ul', function(e) {
  show('委托到ul触发')
})
div.on('mousedown', 'a', function(e) {
  show('委托到a触发')
})
div.on('mousedown', function(e) {
  show('mousedown')
})

给 div 元素绑定一个 mousedown 事件,但是在 div 元素上触发的时候,其实之前还会先触发 li、ul、a,3 个元素的事件,这是因为事件都是绑定的四个事件,3 个是通过委托到 div 元素上触发的,那么这个委托是如何处理的?

设计的思路解析:

委托的实现说起来很简单,我们事件对象里面不是有一个 target 属性吗? target 就指的触发的目标对象。

target 的理解:

<ul>
 <li>点击执行委托链</li>
</ul>
  1. 给 ul 绑定一个事件,那么点击 li 的时候 li 就是目标对象
  2. 如果给 li 绑定一个事件,点击 li 目标对象就是自身了
通过 target 与实际的事件绑定对象我们就可以划分一个区域段,通过递归获取每一个元素的 parentNode 节点,在每一个节点层上通过与委托节点的对比用来确定是不是委托的事件元素,这个就是委托的核心思路了

这个处理 jQuery 交给了 $.event.handlers 方法,就上一个结构我们 handlers 这样分解

  1. 点击触发事件,触发任意一个 target 元素
  2. 通过递归回溯 target.parentNode 来找到 li 元素或者 ul 元素,当然递归的最终直到 div 节点
  3. 如果在任意个递归中找到了委托的节点那么就这个元素给保存起来,这里暂时不处理,这样我们先确定好这个触发的队列数据
简单来说就是把 target 到根节点 div 通过 node.parentNode 遍历一遍,然后找到对应的委托元素节点,如果符合就缓存起来用于之后的操作,可以通过 jQuery.event.handlers 方法我们可以获取类似这种的一组数据结构

那么过滤之后的结构就是这样了:通过 handlerQueue 保存需要的委托队列数据


从这里我们可以看出 delegate 绑定的事件和普通绑定的事件是如何分开的,对应一个元素一个 event.type 的事件,处理对象队列在缓存里只有一个,按照冒泡的执行顺序与元素的从内向外递归以及 handlers 的排序,所以就处理了,就形成了事件队列的委托在前,自身事件在后的顺序,这样也跟浏览器事件执行的顺序一致了。

区分delegate绑定和普通绑定的方法是:delegate 绑定从队列头部推入,而普通绑定从尾部推入,通过记录 delegateCount 来划分,delegate 绑定和普通绑定

我们按照委托的顺序遍历这个结构

while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) {
  event.currentTarget = matched.elem;
  j = 0;
  while ((handleObj = matched.handlers[j++]) && !event.isImmediatePropagationStopped()) {
      ret = handleObj.handler.apply(matched.elem, args);
      //如果返回了false
      if (ret !== undefined) {
        if ((event.result = ret) === false) {
          event.preventDefault();
          event.stopPropagation();
}

因为结构上来说,同一个 div 上绑定多个委托元素,那么事件对象引用就是同样的,event.isPropagationStopped 引用永远是 div 的事件对象 div,ul与 p 都是共用的,只是在不同的元素上面修改delegateTarget 与 currentTarget,所以在之前重写的事件对象就发挥作用了,如果在一个元素上调用了stopPropagation 那么后面的事件自然都不会触发了,因为 event.isPropagationStopped 会获取这个状态
总的来说 jQuery.event.handlers 做的事情:

  1. 将有序地返回当前事件所需执行的所有事件处理程序。
  2. 这里的事件处理程序既包括直接绑定在该元素上的事件处理程序,也包括利用冒泡机制委托在该元素的事件处理程序(委托机制依赖于 selector)。
  3. 在返回这些事件处理程序时,委托的事件处理程序相对于直接绑定的事件处理程序在队列的更前,委托层次越深,该事件处理程序则越靠前。

 

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <script src="http://www.imooc.com/static/lib/jquery/1.9.1/jquery.js" type="text/javascript"></script>
  6. <title>委托实现</title>
  7. </head>
  8. <body>
  9.  
  10.  
  11. <div id="aaron">
  12. <div>
  13. <ul>
  14. <li>点击执行委托链</li>
  15. </ul>
  16. </div>
  17. </div>
  18.  
  19. <p></p>
  20.  
  21. <script type="text/javascript">
  22.  
  23.  
  24. var p = $('p')
  25. function show(data){
  26. p.append('<li>'+ data +'</li>')
  27. }
  28.  
  29. var aaron = $("#aaron")
  30.  
  31. aaron.on('mousedown', 'li', function(e) {
  32. show('委托到li触发')
  33. })
  34.  
  35. aaron.on('mousedown', 'ul', function(e) {
  36. show('委托到ul触发')
  37. })
  38.  
  39. aaron.on('mousedown', 'div', function(e) {
  40. show('委托到div触发')
  41. })
  42.  
  43. aaron.on('mousedown', function(e) {
  44. show('mousedown')
  45. })
  46.  
  47.  
  48. var ul = $('ul')
  49.  
  50. aaron.on('mouseup', 'li', function(e) {
  51. show('mouseup委托到li触发')
  52. })
  53.  
  54.  
  55. aaron.on('mouseup', function(e) {
  56. show('ul mouseup')
  57. })
  58.  
  59. </script>
  60.  
  61. </body>
  62. </html>
下一节