JS之事件(一)

事件,像click,load,mouseover都是用户或者浏览器执行的某种动作,都会触发某些事件。在触发事件的时候,我们需要有一个事件监听函数(事件处理程序)来响应事件。事件监听函数的名字都是“on”+事件名组成的,像click的事件监听函数就是onclick。

HTML事件监听程序

对于元素支持的每种事件,都可以在元素中指定一个与事件监听函数同名的特性。特性的值是要执行的js代码,比如下面的例子:

1
<input type="button" value="clicked me" onclick="console.log('clicked')"/>

如果函数体内容多的话,需要把js代码放到javascript标签内,如下:

1
2
3
function showMessage(){
console.log(‘clicked’);
}

1
<input type="button" value="clicked me" onclick="showMessage()"/>

但是这样指定事件监听函数让得HTML与Javascript紧密耦合,如果需要更换事件监听函数,那么HTML和javascripot代码都需要修改,所以为什么不干脆全部使用js来绑定和声明事件监听函数呢!

js事件监听程序

DOM0级事件处理程序

每个元素都有自己的事件处理程序属性,通常事件名都为小写,如:onclick,onmouseover,在绑定处理函数时需要先获得元素的引用,直接看一个简单的例子吧:

1
2
3
4
5
var btn = doucment.getElementById('button1');
btn.onclick = function(){
console.log(this.id);//button1
}

程序中的this指向的是当前元素,即btn,通过点取的方式,我们可以访问元素的任何属性和方法。

DOM2级事件处理程序

“DOM2级事件”定义了两个用于绑定事件和移除事件的方法:addEventListener()和removeEevntListener(),这两个方法在之前冒泡和捕获那一节已经介绍过了,就不过多说明了。

这两个方法都接收三个参数:

  • 事件名(不带on)
  • 事件处理函数
  • 是否捕获

addEventListener()的优势在于可以为同一元素绑定多个方法,执行顺序由绑定先后决定。

在移除时传入的参数要与绑定时相同,这也意味着不能移除匿名函数

IE事件处理程序

IE实现两个类似的方法:attachEvent()和detachEvent(),之前同样也介绍过,下面就说一下特别的地方。

这两个方法都接收两个参数:

  • 事件名
  • 事件处理函数

DOM0级方法中事件处理程序会在元素的作用域内进行,而使用这两个方法时程序会在全局作用域中运行,因为this指向window。

和addEventListener()类似,attachEvent()可以为元素绑定多个方法,不过是后绑定的先运行。

跨浏览器的事件处理程序

为了让我们的代码可以在所有浏览器正常运行,我们可以自己编写一个兼容所有浏览器的事件处理程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var EventUtil = {
'addHandler':function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type] = handler;
}
}
'removeHandler':function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type] = null;
}
}
}

事件对象

这算一个比较重要的点了,我们触发某个事件时,会产生一个事件对象event,其中包含着所有与时间有关的信息,如事件的类型,事件的目标(事件委托应用到),以及与特定事件相关的信息。例如,鼠标事件中,有包含鼠标位置的信息,键盘事件中则包含于按键相关的信息。浏览器虽然都支持event对象,但支持方式不尽相同。

DOM中的事件对象

当触发了事件时,兼容DOM的浏览器都自动会给我们的事件处理程序传入一个event对象,我们要做的只是在声明监听函数的时候加上一个event参数以便浏览器传入就ok了,如下:

1
2
3
4
5
6
btn.onclick = function(event){
...
}
btn.addEventListener = function(event){
...
}

下面列举type的属性及方法:

  • type:事件类型
  • bubbles:表明事件是否可以冒泡
  • cancelable:表明是否可以阻止默认事件
  • currentTarget:事件处理程序当前正在处理的元素,通常用target较多
  • preventDefault():取消事件的默认行为,需要cancelable属性为True方可用,可用来定制自己的处理行为
  • stopPropagation():取消事件的进一步捕获或者冒泡,需要bubbles为True方可用
  • target:事件的真正目标
  • eventPhase:确定事件当前正处于事件流的哪个阶段,1代表处于捕获阶段,2代表处于目标对象上,3代表处于冒泡阶段

在事件处理程序内部,this始终等于currentTarget的值,如果不使用事件委托,那么this,currentTarget,target的值相同.

IE中的事件对象

访问IE中的event的方式有几种,取决于绑定监听函数的方式。

如果是使用DOM0级方法添加的事件监听函数,那么event对象会作为window对象的一个属性存在。

1
2
3
4
5
var btn = document.getElementById("button1");
btn.onclick = function(){
var event = window.event;
console.log(event.type);
}

如果通过attachEevent方法来绑定监听函数,那么event对象会自动传入我们我们的事件处理函数中。

1
2
3
4
var btn = document.getElementById("button1");
btn.attachEvent("onclick",function(event){
console.log(event.type);
})

同样,IE下的event对象也有一些属性和方法,大多都与DOM下的event属性方法对应:

  • cancelBubble:取消冒泡,默认为false,与stopPropagation()对应
  • returnValue:默认为true,置为false可阻止默认事件,与preventDefault()对应
  • srcElement:与Dom中的target属性对应
  • type:同DOM下的type

IE中事件处理程序中的this会随绑定方式的不同而变化,所以最好使用srcElement比较稳妥。

跨浏览器的事件对象

下面来实现事件对象的兼容写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var eventUtil = {
getEvent:functionevent{
return event?event:window.event;
}
getTarget:function(event){
return event.target || event.srcElement;
}
preDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
}
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
}

事件类型

介绍完基本的事件监听函数和事件对象,接下来就应该介绍种类繁多的事件类型及应用了。

DOM3级事件规定了一下事件:

  • UI(user Interface)事件,用户与元素交互时触发
  • 焦点事件,元素获取或失去焦点时触发
  • 鼠标事件,通过鼠标执行操作时触发
  • 滚轮事件,使用滚轮时触发
  • 文本事件,在文档中输入文本时触发
  • 键盘事件,在键盘上执行操作触发
  • 合成事件,在IME(输入法编辑器)输入字符触发
  • 变动事件,底层DOM结构发生变化时触发

包括IE9在内的主流浏览器斗殴支持DOM2级事件,IE9也支持DOM3事件

UI事件

UI事件指的并不一定是与用户操作有关的事件,较常用的有:

  • load:页面完全加载后在window上触发,当所有框架加载完在框架集上触发,当图像加载完毕是在img元素上触发
  • unload:与上面类似,只不过是在写在的时候触发
  • select:用户选择文本框中的字符时触发。
  • resize:当窗口或框架大小发生改变时触发
  • scroll:用户滚动带滚动条的元素中的内容时触发

这些事件都可以用HTML事件处理程序的方式实现。

根据DOM级事件规范,我们应该在document上处理load事件,但实际上所有浏览器在window上都实现了该事件,确保向后兼容。

焦点事件

焦点事件与document.hasFocus()及document.activeElement属性配合,可以知道用户在页面上的行踪。

焦点事件有以下这些:

  • blur:元素失去焦点时触发,不会冒泡
  • focus:元素获取焦点是触发,不会冒泡
  • focusin:元素获取焦点时触发,冒泡,除了FF外基本都支持(DOM3)
  • focusout:元素失去焦点触发,冒泡,除了FF外基本都支持(DOM3)
  • DOMFocusOut:只有Opera支持,与focusout等价
  • DOMFocusIn:只有Opera支持,与focusin等价

在页面中当焦点从一个元素移到另一个焦点上时,事件的触发顺序是这样的:


  1. focusout在失去焦点的元素上触发

  2. focusin在获得焦点的元素上触发

  3. blur在失去焦点的元素上触发

  4. DOMFocusOut在失去焦点的元素上触发

  5. focus在获得焦点的元素上触发

  6. DOMFoucusIn在获得焦点的元素上触发

要确定浏览器是否支持dom3的焦点事件,可以用以下代码

1
var isSupported = document.implementation.hasFeature("focusEvent","3.0");

鼠标和滚轮事件

网页中用户大多的操作行为都是通过鼠标来实现的,DOM3级中实现了9个鼠标事件:

  • click:用户单击鼠标左键和enter键时触发
  • dblclick:用户双击鼠标左键时触发,DOM3将它纳入了标准
  • mousedown:用户按下鼠标按钮还没放开时触发
  • mouseup:用户释放鼠标按钮时触发
  • mouseenter:鼠标光标首次移进目标元素内触发,此事件不冒泡,移进后代元素不会触发,DOM3将它纳入了标准
  • mouseoutleave:鼠标光标移到元素范围之外时触发,事件不冒泡,移进后代元素不会触发,DOM3将它纳入了标准
  • onmouseover:鼠标光标首次移进目标元素内触发,移进后代会触发
  • onmouseover:鼠标光标移出目标元素内触发,移进后代会触发

总结:除mouseenter和mouseleave外,其他函数都会冒泡,也可以被取消默认行为。只有相继触发mousedown和onmouseup才会触发click事件。连续两次触发click才会引发dblclick事件。

这四个事件触发的先后顺序如下:


  1. mousedown

  2. mouseup

  3. click

  4. mousedown

  5. mouseup

  6. click

  7. dblclick


要确定浏览器是否支持DOM3和DOM3的鼠标事件,可以用以下代码

1
2
var isSupported2 = document.implementation.hasFeature("MouseEvents","2.0");
var isSupported3 = document.implementation.hasFeature("MouseEvent","2.0");

滚轮事件

鼠标能触发的还有一个滚轮事件,mousewheel,其实之前的博客中已经做了详细的介绍了。

鼠标事件中事件对象具有以下属性:

  • clientX:事件发生时鼠标在视口中的水平坐标
  • clientY:事件发生时鼠标在视口中的垂直坐标
  • pageX:事件发生时鼠标在页面中的水平坐标(包含了滚动的距离)
  • pageY:事件发生时鼠标在页面中的垂直坐标(包含了滚动的距离)

    以pageY为例,使用clientY和滚动信息就可以计算出pageY,这里我们需要通过document.body(混杂模式)和document.documentElement(标准模式)中的scrollLeft和scrollTop。

    pageY = event.clientY + document.body.scrollTop || document.documentElement.scrollTop

  • screenX:事件发生时鼠标距离屏幕左边的距离
  • screenY:事件发生时鼠标距离屏幕上方的距离
  • 修改键:

    • 有时我们会结合键盘和鼠标来修改鼠标事件,这时就要使用类似Shift、Ctrl、Alt和Meta之类的修改键了,有4个表示这些修改键状态的属性:
      • shiftKey
      • ctrlKey
      • altKey
      • metaKey

mousewheel事件触发后,会冒泡到document或者window,此外,该事件对应的事件对象中有一个wheelDelta属性,当滑轮向上滚是,该属性值是120的倍数向下滚时,该属性值是-120的倍数.
FF(fireFox)还支持一个DOMMouseScroll事件,与mousewheel类似,只不过该事件的信息存放在event.detail(Opera也是)中,属性值为-3的倍数时,为滑轮向前滚;为3的倍数时,为滑轮向后滚。

为避免篇幅过长,其它事件在下一篇博客进行介绍。