关于javascript:事件合成知多少

41次阅读

共计 1990 个字符,预计需要花费 5 分钟才能阅读完成。

ps: 本文首发于公众号 话说前端

在目前的我的项目开发中,因为应用的 angular 1.x 来进行利用层面的开发,那么在处理事件的时候就不得不应用框架本身所带的 ng-click指令来进行事件的绑定,应用 ng-click 进行绑定的话,对于长列表,交互事件多的中央来说,不是一个好的抉择。因为会绑定很多的事件。于是就寻求以一种适合的事件代理的形式来尝试代替(这里只探讨 click)。所以来做个总结

要实现这件事,无妨先回顾一下事件流的一些常识

如上图所示,当你点击页面上的元素的时候,事件会顺次达到这三个阶段,它们别离是:

    事件捕捉阶段 -> 指标阶段 -> 冒泡阶段。

单拿出冒泡阶段来说,意味着当你点击了某一个具体的元素后,那么最初你是能够在 document 这个层级接管到事件的,这就形成了代理的根底。

接下来就要思考的问题是:

  1. 我在根节点上的监听办法如何响应组件外面定义的回调函数
  2. 咱们的写法如何与 ng-click 保持一致。
  3. 实现事件合成的冒泡与阻止冒泡

先来看失常的 ng-click 指令是如何应用的,如下:

<example
  ng-click="test($event,item)"
></example>

所以只须要实现一个指令,接管一个函数即可(这里只探讨属性为函数的场景)。

回调函数的查找

const clickDirective = function () {
  return {
    restrict: 'A',
    link(scope, element, attr) {
      try {if (!attr.mmClick) return;
        const {fn, functionName, paramsArr} = service.parseParams(attr.mmClick, scope);
        // eslint-disable-next-line
        element[0][`rcs-${functionName}`] = {fn, paramsArr};
      } catch (e) {console.error(e);
      }
    },
  };
};

如上,咱们能够在指令的初始化中,将咱们获取到的回调函数以及参数以特定名称的形式挂载到 dom 树上
这里以 rcs 字符结尾,这样就能在根节点接管的事件中去寻找到这个回调函数并解决

function executeEvent(target, e) {const keys = Object.keys(target);
  for (let i = 0; i < keys.length; i++) {if (keys[i].indexOf('rcs') === 0) {const { fn, paramsArr} = target[keys[i]];
      fn.call(this, ...paramsArr, { e});
    }
    // 解决是否须要冒泡
    if (e.hasOwnProperty('stopBubble') && e.stopBubble) {return;}
  }
  if (target.parentNode !== null) {return executeEvent(target.parentNode, e);
  }
}

能够看到,下面的函数是一种递归的形式,逐级寻找回调函数,并执行它。这样就实现了函数的定义与执行局部。

合成事件的冒泡与阻止冒泡

与此同时,在一个 dom 层级上,能够在不同的层级绑定事件,这样在冒泡阶段,会顺次执行。那么在如上的函数中也体现了这点。

如上的函数,会把事件对象通过参数的模式给到调用方(别问为啥在最初,因为要兼容已有函数定义),

fn.call(this, ...paramsArr, { e});

在回调函数里,只须要设置这个值即可:

$scope.showGroupCard = function (item, chatInfoType, { e}) {e.stopBubble = true;}

当这个值为 true 的时候,那么 executeEvent 就会终止执行,否则会直到 parentNodenull

最初再来看看应用成果:

<example
  mm-click="test(item)"
></example>

其实到这里,这个事件合成简易版就算实现了。尽管是利用的 dom 本身的层次性,但查找速度齐全不必放心。

另外就是对于回调函数外面的 this 的问题,回调函数须要应用箭头函数来穿透才行,问题不大

函数参数解析

其实这里最难的局部在于指令的函数解析上,一个函数它的参数能够有如下几种(可能我还没列举完):

  • 简略的根本数据类型
  • 变量(这个变量可能来自本人组件的作用域,来自父组件传递下来的作用域,也可能来自根作用域)
  • 参数是一个函数,或者是一个递归函数
  • 参数是一个对象,对象外面有变量
  • 等等

所以基于此,再结合实际的业务场景,约定了如下:

  • 指令的属性只能是一个函数,不承受其余值
  • 只保留了以后作用域变量,以后变量的调用,舍弃了表达式等写法,
  • 函数参数只是以后组件作用域及作用域链上的,如果须要应用表达式或者其余写法,就把它挂载到以后作用域上。

end

万物起于微忽,质变引起量变

正文完
 0