自定义浏览器事件,模拟浏览器事件流

来源:互联网 发布:云计算 erp 编辑:程序博客网 时间:2024/04/28 14:29

简述

我认为,事件是我们前端最为熟悉的编程模式,在前端开发中能接触太多太多,而且相对而言,事件是一种相对容易理解,逻辑性高的的模式,对于优化组件/插件的逻辑是一个很好的应用模式。

这文章主要是用JavaScript实现3级dom事件机制,后面的更新会涉及应用倒组件开发当中。

  1. 浏览器事件一:事件的基础应用
  2. 浏览器事件二:浏览器是如何实现事件
  3. 浏览器事件三:自定义事件

如何实现

jQuery 实现方式

jQuery 是实现了一个独立的事件方法,甚至可以在自定义对象(object)上绑定事件,功能十分强大。
例:

//import jQuery$(selector).on('yourEvent',function(){    console.log('i m yourEvent handler');})$(selector).trigger('yourEvent')

上面例子就是jQuery 的自定义事件实现方式,下面再展示一个绑定对象上的事件,它其实是一个订阅/发布模式的实现 —> cowboy 实现的简易订阅/发布
例:

(function($) {  var o = $({});  $.subscribe = function() {    o.on.apply(o, arguments);  };  $.unsubscribe = function() {    o.off.apply(o, arguments);  };  $.publish = function() {    o.trigger.apply(o, arguments);  };}(jQuery));

jQuery的事件实现原理这里不作陈述,这里提供Aaron的jQuery源码解析传送门

jQuery实现的优点

  1. 良好的维护性
    jQuery是一个使用率最高的前端库,这意味这它能提供非常友好的文档,以及学习社区。从项目的角度上来看这是提高维护性的一个做法。
  2. 良好的兼容性
    解决兼容性是意见相当麻烦琐碎的事情,如果没有充足的经验,很难再编码的的开始就预计到那些兼容问题,所以jQuery 本身某种层度就是提供兼容处理封装。

jQuery实现的缺点

  1. 依赖jQuery
    实现上,我们并不是所有项目都需要依赖jQuery,jQuery本身虽然十分好,但是jq链式操作dom写出来的页面/组件,再可读性上确实不尽人意,业界都是认同那是一坨意大利面(特别是webapp项目)。所以其实从另一个层面上看,jQuery并不是一个十分好提高维护性的方案。
  2. 能实现的功能有限
    如果单纯做组件抽象,或者基于监听响应实现的对象,jq的实现确实是满足并且能提供简洁的实现方法,但是在某些需求上不一定能满足,如:如果我们希望组件的实现逻辑更加清晰,希望加入一个事件响应流,能在组件之间链式传播。
  3. 性能上
    jQuery事件实现内部也是已经实现事件的,如果在jq之上在添加自定义的事件功能,性能上必然有影响(不可控),性能影响不会很大,但做性能优化时会是一个问题。

任意对象绑定事件

在原生JavaScript对象,自定义的object 对象并没有事件的概念, 而实际上 我们希望某些组件拥有事件驱动,js拥有完整的事件驱动对象,但是我们开发者没有办法使用(第二章有提及),不过我们还是能基于官方的文档事件一个与js一样的事件对象。

官方自定义 eventtarget

我们从官方的事件文档中看到一个简单的事件驱动对象实现例子。

var eventTarget = function() {  this.listeners = {};};eventTarget.prototype.listeners = null;eventTarget.prototype.addEventListener = function(type, callback) {  if(!(type in this.listeners)) {    this.listeners[type] = [];  }  this.listeners[type].push(callback);};eventTarget.prototype.removeEventListener = function(type, callback) {  if(!(type in this.listeners)) {    return;  }  var stack = this.listeners[type];  for(var i = 0, l = stack.length; i < l; i++) {    if(stack[i] === callback){      stack.splice(i, 1);      return this.removeEventListener(type, callback);    }  }};eventTarget.prototype.dispatchEvent = function(event) {  if(!(event.type in this.listeners)) {    return;  }  var stack = this.listeners[event.type];  event.target = this;  for(var i = 0, l = stack.length; i < l; i++) {      stack[i].call(this, event);  }};

这个例子可以是我们实现自己事件的最顶层类。

如何绑定到自定义对象

假设我们已经有了 eventTarget对象,下面我们要做的就是把事件绑定到对象上

var component = function(){};component.prototype = new eventTarget();var com_1 = new component();com_1.addEventListener('click',function(){    console.log('i m listenning click event')});com_1.dispatchEvent(new MouseEvent('click')) //i m listenning click event

加入htmlElement事件流

htmlElement的事件流是独立的,实际上的C++在做事件流的传递,自定义的事件没法加入到这个事件流当中。
这种冒泡的事件流机制非常常见,在ios,Android中的事件驱动都是类似这种机制。在事件中这样的思想/机制能把事件的耦合度降低。在大型组件的事件逻辑中,加入这种机制个人认为是非常好的。

这里提供一个实现思路,鄙人愚见,请大神指点。
这里写图片描述

创建自己的对象的事件流

 var  flowEvent = function (parentNode) {        this.listeners = {};        this.childrenNode = [];        this.parentNode = parentNode; // 父节点        parentNode && parentNode.childrenNode.push(this) //加入父节点的子节点}flowEvent.prototype = new eventTarget(); //继承事件flowEvent.prototype.dispatchEvent = function (event) { //重写事件分发方法    if(!(event.type in this.listeners)) {        return;    }    var stack = this.listeners[event.type]; //要独立lisenter    event.target = this;    for(var i = 0, l = stack.length; i < l; i++) {        stack[i].call(this, event);    }    if(event.target){    //todo 加入阻止同类事件&阻止传递        this.childrenNode.length>0 && this.childrenNode.forEach(function (v) {            v.dispatchEvent(event)        })    }else{        this.parentNode && this.parentNode.dispatchEvent(event)    }}
0 0
原创粉丝点击