事件

博客分类: 原创

事件

JavascriptHTML之间的交互是通过事件实现的。

事件流描述的是从页面中接受事件的顺序。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获。

事件流的三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

事件流

[1-4]是事件捕获阶段 [4-5]是处于目标阶段 [5-9]是事件冒泡阶段

各浏览器的事件触发

IE只支持时间冒泡,不支持事件捕获,IE的事件流只有事件从发生的目标开始,沿着文档逐层向上冒泡到document为止,其他浏览器支持捕获也支持冒泡。很明显如果在捕获阶段触发的话,事件会被触发两次。在冒泡阶段捕获只会触发一次。

W3C事件模型中你可以选择是在捕获阶段还是冒泡阶段绑定事件处理函数,这是通过addEventListener()方法实现的,如果这个函数的最后一个参数是true,则在捕获阶段绑定函数,反之false,在冒泡阶段绑定函数。

element1.addEventListener('click',doSomething1,true)    // 捕获阶段触发
element2.addEventListener('click',doSomething2,false)    // 冒泡阶段触发

这种模型下,还可以把事件冒泡关掉。在回调函数doSomething中,可以得到一个目标事件对象参数,这里取值为e。可以使用e.stopPropagation(),关掉从这个事件之后的事件冒泡。还有个常用方法是e.preventDefault(),这个是阻止目标事件的默认事件的触发,比如<a>标签的跳转,<button>按钮的提交等。千万不要记混了。我就是那个记混了好多年的人。

IE事件模型中不支持事件捕获,所以也就不会有第三个参数,使用方法也变了。

element.attachEvent("onclick", doSomething);

IE事件模型也有阻止事件冒泡的方法window.event.cancelBubble = true;,也有阻止事件的默认事件的触发window.event.returnValue = false;

还有些事件本身就不会冒泡blur、focus、load、unload

自定义事件

javascript中自定义事件可以让代码结构更清晰,更高效,大部分代码的构建都是基于事件机制的。比如中介者模式,广播模式等等。我认为事件机制也是javascript的核心,所以有必要知道事件机制的大概实现。以下是自己写的一个大概的实践。实例化或者继承都可以拿到这个事件,然后可以调用自身的一些方法,来达到传递事件的目的。

var EventTarget = function() {
    // 事件存储的私有对象
    this._listener = {};
};

EventTarget.prototype = {
    // 添加事件
    addEvent: function(type, fn) {
        if (typeof type === 'string' && typeof fn === 'function') {
            if (typeof this._listener[type] === 'undefined') {
                this._listener[type] = [fn];
            } else {
                this._listener[type].push(fn);
            }
        }
        return this;
    },
    // 触发事件
    fireEvent: function(type) {
        if (type && this._listener[type]) {
            var events = {
                type: type,
                target: this
            };

            for (var length = this._listener[type].length, start=0; start<length; start+=1) {
                this._listener[type][start].call(this, events);
            }
        }
        return this;
    },
    // 去掉事件
    removeEvent: function(type, key) {
        var listeners = this._listener[type];
        if (listeners instanceof Array) {
            if (typeof key === 'function') {
                for (var i=0, length=listeners.length; i<length; i+=1){
                    if (listeners[i] === key){
                        listeners.splice(i, 1);
                        break;
                    }
                }
            } else if (key instanceof Array) {
                for (var lis=0, lenkey = key.length; lis<lenkey; lis+=1) {
                    this.removeEvent(type, key[lenkey]);
                }
            } else {
                delete this._listener[type];
            }
        }
        return this;
    },
};

// 使用方法
// 实例化出一个事件对象载体
var testEvent = new EventTarget();
// 注册事件
testEvent.addEvent('hello', function(){
    console.log('hello');
});
// 触发事件
testEvent.fireEvent('hello');

以上就是一个简单的自定义事件的方法,同样你要实现命名空间怎么办?array.split('.'),然后把这个结构存在_listener中,上面的方法都改一下也就可以实现命名空间了。如何实现批量绑定时间呢。就是把所有事件写成一个数组对象。

自定义DOM事件

有时候你觉得IE的事件机制和标准你老是要写兼容,而这种兼容你又不想那么麻烦,可以自定义DOM事件来做。但是自定义DOM事件不支持IE7以下的浏览器,所以也需要你做抉择。自定义事件的方法是createEvent()创建event对象。

自定义时间支持四类事件:

  • UIEvents:一般化的 UI 事件。鼠标事件和键盘事件都继承自 UI 事件。DOM3 级中的是 UIEvent。
  • MouseEvents:一般化的鼠标事件。DOM3 级中的是 MouseEvent。
  • MotationEvents:一般化的 DOM 变动事件。DOM3 级中是 MutationEvent。
  • HTMLEvents:一般化的 HTML 事件。没有对应的 DOM3 级事件( HTML 事件被分散到其他类别中)。

使用方式:

// 新增一个 DOM 的自定义事件
var event = document.createEvent('MouseEvents');
// 初始化一个 DOM 的自定义事件
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
document.addEventListener('click', function (e) {
    console.log('click fire', e);
}, false);
// 触发一个自定义事件
document.dispatchEvent(event);
// 由于以上事件是 DOM 的 click 事件,所以点击也可以触发

// 其他自定义事件
// 监听事件,即使这个事件还没有定义,我们一样可以绑定他,但是这个时候的绑定是没有动作来触发他的。
// 当我们把这个事件定义好之后,触发了该事件,该事件也可以接收到。
document.addEventListener('build', function (e) {
    console.log(e)
}, false);

// 新增一个自定义事件
var event = document.createEvent('Event');

// 初始化一个自定义事件
event.initEvent('build', true, true);

// 同时你可以在这个上面挂在属性,使得触发该事件时,e 属性中会具有你的挂载的属性及方法。任何自定义事件都可以挂在属性
event.name = 'karyn'

// 触发事件
document.dispatchEvent(event);

// 执行结果
// Event {name: "karyn"}
// 除了主动挂在的属性外,还有很多默认方法是放在 Event 的 prototype 中的
// bubbles: true
// cancelBubble: false
// cancelable: true
// currentTarget: null
// defaultPrevented: false
// eventPhase: 0
// name: "karyn"
// path: Array[2]
// returnValue: true
// srcElement: document
// target: document
// timeStamp: 1444749196538
// type: "build"

关注自定义事件吧,这将是大事件开启的桥梁。hybrid的开发方式就是用这样的事件来沟通前端和native

事件机制是javascript的核心,所以在理解和使用的时候也希望自己能够清楚理解其含义。能够知道自己在写代码时,或者架构页面时自己能够懂自己写的是啥,特别是在学习node的过程中,事件机制更是贯穿始终,希望自己把这个理解透彻。