事件是HTML与JavaScript的通讯方式,也就是经常听到的基于事件的通讯方式。我们需要先在某个元素对象上面监听某一类事件,然后将某个回调函数绑定到这个事件上面,当事件发生的时候,回调函数就会被立刻调用。所有需要传递给回调函数的数据都绑定在事件对象上面,回调函数可以接受这个事件对象,然后从其中取出自己需要的数据执行逻辑上的操作。其实这种模式就是软件工程中很有名的观察者模式,该模式主要的目的是实现HTML与JavaScript之间的松耦合。当HTML方的某个事件发生的时候,它只负责封装事件对象,并传递这个事件对象,而不需要关心该事件发生之后会有什么样的逻辑处理。而JavaScript方只负责接收事件对象,并且知道怎么样去处理这个事件对象和执行逻辑操作,而不关心到底怎么样去产生这个事件对象。所以他们之间是一种基于基于事件的松耦合的通信方式。
1. 事件流
大部分浏览器都已经实现DOM2的事件流方式,但是IE依然保持自己的事件方式(冒泡事件)。
事件冒泡:
事件由最具体的元素(即最底层的那个节点)开始,一层一层向上一层传播到document(或者window)为止。例如有在某个页面中有一个div,如果你点击这个div,那么在IE中的传播方式如下:
1. <div> 最具体的元素
2. <body>
3. <html>
4. document 可以到达的最顶层的元素
事件捕获:
事件捕获与时间冒泡正好相反,由接受事件的最顶层的元素开始,逐层向下传播,直到最具体的元素。所以上面的例子就会变成这个样子:
1. document 接收事件最顶层的元素
2. <html>
3. <body>
4. <div> 最具体的元素
DOM事件流:
DOM事件流包括3个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段。所以上面的例子如果用DOM事件流表示就是:
1. document 事件捕获阶段开始
2. <html>
3. <body> 事件捕获阶段结束
4. <div> 处于目标阶段
5. <body> 事件冒泡阶段开始
6. <html>
7. document 事件冒泡阶段结束
2. 事件处理程序
我们把可以响应某个事件的函数称为事件处理程序。事件处理程序分为以下几种:
HTML 事件处理程序
直接在元素标签中指定处理程序的方式:
<input type="button" click="alert('Hello, leo!')" />
<input type="button" click="sayHello('leo')" />
这种方式虽然直观,但是有很多缺点。首先耦合性过分紧密,如果我们需要改变sayHello变成showHello,那么我们可能需要改变所有使用sayHell的地方;其次如果我们将sayHello写在了调用它的按钮后面,当浏览器的DOM树加载完成后,用户就可以和浏览器交互了,意味着这个按钮就可以点击了。这个时候如果脚本文件还没没有加载完成,那么就可能出现调用空函数的情况,即浏览器会报错。所以建议不要使用这种方式绑定事件处理程序。
DOM0级事件处理程序
即使到现在,几乎所有浏览器依然支持这种模式。写法如下:
var btn = document.getElementById('myBtn');
btn.onclick = function() {
alert( 'Hello, leo!' );
alert( this.id );
}
这种方式的特点是,回调函数会在btn的作用域中执行,即this就指向btn;而且需要在事件名前面加“on”。如果需要删除btn的事件处理程序,可以这样写:btn.onclick = null; 。
DOM2级事件处理程序
DOM2级事件定义两个方法来绑定和移除事件处理程序:addEventListener 和 removeEventListener 。
btn.addEventListener( 'click', sayHello, fasle );
btn.removeEventListener( 'click', sayHello, false );
btn.addEventListener( 'click', function(){...}, false ); 匿名函数方式
参数1:事件名;
参数2:回调函数,当然这里也可以使用匿名函数,不过缺点显而易见。不仅不利于代码维护,而且无法通过reomveEventListener移除,因为你即使申明一个和绑定时候一摸一样的匿名函数,但是他们依然是两个不同的函数,所以无法通过匿名函数的方式移除事件处理程序;
参数3:false代表在冒泡阶段触发,true代表在捕获阶段触发。由于IE只支持冒泡,所以通常是false。
使用DOM2的事件处理程序方式可以为同一个对象添加多个事件处理程序,多个处理程序会按照添加的顺序依次被触发。而且回调函数也是在btn的作用域下执行,即this指向btn。
IE事件处理程序
IE提供了两个类似的方法:attachEvent 和 detachEvent。
btn.attachEvent( 'onclick', sayHello );
btn.detachEvent( 'onclick', sayHello );
与DOM2级类似的地方是:同样可以添加多个事件处理程序,也不能通过匿名的方式移除事件处理程序。
与DOM2级不同的是:添加的多个事件处理程序不按照添加的顺序依次触发,而是相反的顺序,并且this指向的是window而不是btn。IE只支持冒泡,所以不需要第三个参数。
兼容性的事件处理程序
为了兼容各种浏览器的方式,所以我们需要封装一些这些不同的处理方式,提供一个统一的接口:
function addEvent( element, type, handler ) {
if ( element.addEventListener ) { // DOM2级的处理方式
element.addEventListener( type, handler, false );
} else if ( element.attachEvent ) { // IE的处理方式
element.attachEvent( type, handler );
} else { // DOM0的处理方式
element[ on + 'click' ] = handler;
}
}
移除事件处理程序也是类似的书写方式。
3. 事件对象
DOM事件对象:
通过DOM方式(无论是DOM0还是DOM2)绑定的回调函数,在被调用的时候,都会接受一个事件对象作为第一个参数出入。不同类型的事件对象包含了自己的特定的属性和方法,但是他们有一下共用的属性和方法如下:
属性/方法 类 型 说 明
bubbles: Boolean 表明事件否可以冒泡
cancelable Boolean 表明是否可以取消事件的默认行为
currentTarget Element 事件处理程序当前正在处理的的那个元素
deatil integer 相关的细节信息
eventPhase integer 1表示捕获阶段,2表示处于目标阶段,3表示冒泡阶段
target Element 事件的目标
type String 事件的类型
view AbstractView 发生事件的抽象视图
preventDefault Function 如果cancelable是true,调用它就可以取消事件的默认行为
stopPropagation Function 调用它可以取消事件的传播(捕获或者冒泡)
IE事件对象
IE有自己的事件对象,传入回调函数的方式也有点不一样。对于IE中的DOM0方式,不会传入事件对象,而是需要通过window.event去取出最近的一次事件对象。对于DOM2级的方式,则和标准的一样,但是也可以通过window.event取出事件对象。关于IE事件对象的共有属性和方法:
属性/方法 类 型 说 明
cancelBubulle Boolean 设置为ture就会取消事件冒泡,与stopPropagation功能类似
returnValue Boolean 默认为ture,设置为false就会阻止默认行为,与preventDefault功能类似
srcElement Element 事件的目标(相当于DOM中的target)
type String 事件的类型
兼容性的事件对象
function getEvent( evt ) {
return evt ? evt : window.event;
}
function preventDefault( evt ) {
if ( evt.preventDefault ) {
evt.preventDefault;
} else {
evt.returnValue = fasle;
}
}
function stopPropagation( evt ) {
if ( evt.stopPropagation ) {
evt.stopPropagation();
} else {
evt.cancelBubble = true;
}
}
4. 事件类型
UI事件:用户和页面进行交互触发的事件;
鼠标事件:用户使用鼠标操作在页面上触发的事件;
键盘事件:用户使用键盘操作在页面上触发的事件;
HTML事件:浏览器窗口发生变化或者发生服务器交互触发的事件;
变动事件:DOM结构发生变化的时候触发的事件。