JavaScript高级程序设计笔记(13)-事件

事件流

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

事件冒泡

IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。

事件捕获

Netscapte Communicator事件捕获(event capturing),不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。

DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。

IE9、Opera、FireFox、Chrome和Safari都支持DOM事件流,IE8及更早版本不支持DOM事件流。

事件处理程序

HTML事件处理程序

某个元素支持特定的某种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。

DOM0级事件处理程序

使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此事件处理程序是在元素的作用域中运行,程序中的this引用当前元素。

1
2
3
4
var btn = document.getElementById('myBtn');
btn.onclick = function(){
alert(this.id);//'myBtn'
}

删除DOM0级方法指定的事件处理程序

1
btn.onclick = null;

DOM2级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有DOM节点都包含这两个方法,接收三个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值如果为true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。

1
2
3
4
5
6
7
var btn = document.getElementById('myDiv');
btn.addEventListener('click', function(){
alert(this.id);
}, false);
btn.addEventListener('click', function(){
alert('hello world');
}, false);

使用DOM2级方法添加事件处理程序主要好处是可以添加多个事件处理程序。
通过addEventListener()添加的事件处理程序,只用使用removeEventListener()来移除。移除时,传入的参数与添加处理程序时使用的参数相同。也意味着通过addEventListener()添加的匿名函数无法移除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var btn = document.getElementById('myDiv');
btn.addEventListener('click', function(){
alert(this.id);
}, false);

btn.removeEventListener('click', function(){//没有用,不能移除匿名函数
alert(this.id);
}, false);


var btn = document.getElementById('myDiv');
var handler = function(){
alert(this.id);
}
btn.addEventListener('click', handler, false);

btn.removeEventListener('click', handler, false);//有效

IE事件处理程序

IE实现了与DOM中类似的方法:attachEvent()和detachEvent()。两个方法接受相同的两个参数:事件处理程序名称与事件处理函数。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会添加到冒泡阶段。

1
2
3
4
5
var btn = document.getElementById('myDiv');
btn.attachEvent('onclick', function(){
//注意事件处理程序名称 'onclick',与DOM的addEventListener()方法中的'click'不同。
alert(this.id);
});

IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用于。在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。

1
2
3
4
var btn = document.getElementById('myDiv');
btn.attachEvent('click', function(){
alert(this === window);//true
})

通过attachEvent()方法为同一个元素添加不同的事件处理程序,与DOM方法不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。

1
2
3
4
5
6
7
8
var btn = document.getElementById('myDiv');
btn.attachEvent('click', function(){
alert('click');
})
btn.attachEvent('click', function(){
alert('hello');
})
//先输出hello, 再输出click

使用attachEvent()方法添加的事件可以通过detachEvent()方法来移除,条件是必须提供相同的参数。与DOM方法一样,添加的匿名函数不能被移除。

跨浏览器事件处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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;
}
}
}

//使用

var btn = document.getElmentById('myDiv');
var hanlder = function(){
alert('clicked');
}

EventUtil.addHandler(btn, 'click', handler);

EventUitl.removeHandler(btn, 'click', handler);

事件对象

在触发DOM上的某个事件时,会产生一个事件对象evnet, 这个对象中包含着所有与事件有关的信息。包括导致事件的元素,事件的类型以及其他与特定事件相关的信息。

DOM中的事件对象

属性/方法类型读/写说明
bubblesBoolean只读表明事件是否冒泡
cancelableBoolean只读表明是否可以取消默认事件
currentTargetElement只读事件处理程序当前正在处理事件的对象
defaultPreventedBoolean只读为true表示已经调用了preventDefault()方法
detailInterger只读与事件相关的细节信息
eventPhaseInterger只读调用事件处理程序的阶段:1表示捕获阶段,2表示处于目标阶段,3表示冒泡阶段
preventDefault()Function只读取消事件的默认行为。如果cancelable是true,可以使用该方法
stopImmediatePropagation()Function只读取消事件的进一步捕获或冒泡,同时阻止任何处理程序被调用。
stopPropagation()Function只读取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用该方法。
targetElementElement事件的目标
trustedBoolean只读为true表示事件是浏览器生成的,为false表示事件是由开发人员通过Javascript创建的
typeString只读被触发事件的类型
viewAbstractView只读与事件关联的抽象视图。等同于发生事件的window对象。

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值。

1
2
3
4
5
var btn = document.getElementById('myBtn');
btn.onclick = function(event){
alert(event.currentTarget == this);//true
alert(event.target == this);// true
}

如果处理程序存在于按钮的父节点

1
2
3
4
5
doument.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); // true
alert(event.target === document.getElementById('myBtn'));// true
}

通过一个函数处理多个事件时,可以使用type属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var btn = document.getElementById('myBtn');
var handler = function(event){
switch(event.type){
case 'click':
alert('clicked');
break;
case 'mouseover':
alert('mouseover');
break;
case 'mouseout':
alert('mouseout');
break;
}
}
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

阻止特定事件的默认行为,preventDefault()。只有cancelable属性为true的事件,才可以使用preventDefault()来取消默认行为。

1
2
3
4
5
var link = document.getElmentById('myLink');

link.onclick = function(event){
event.preventDefault();
}

stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。

IE中的事件对象

与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。
DOM0中,event对象作为window对象的一个属性存在。

1
2
3
4
5
var btn = document.getElementById('myBtn');
btn.onclick = function(){
var event = window.event;
alert(evnet.type);//'click'
}

如果事件是使用attachEvent()添加的,那么就会有一个event对象作为参数被传入事件处理程序函数中。

1
2
3
4
var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function(event){
alert(event.type);
})

通过HTML特性指定的事件处理程序

1
<input type='button' onclick='alert(event.type)'>

IE中,event对象相关的属性和方法。

属性/方法类型读/写说明
cancelBubbleBoolean读/写默认为false,但将其设置为true,就可以取消事件冒泡(与DOM中的stopPropagation()方法的作用相同)
returnValueBoolean读/写默认为true,但将其设置为false,就可以取消事件莫任性(与DOM中的preventDefault()方法的作用相同)
srcElementElement只读事件目标(与DOM中的target相同)
typeString只读被触发的事件类型

跨浏览器事件对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var EventUtil = {
addHandler: function(element, type, handler){
//
},
getEvent: function(event){
return event ? event:window.event;
},
getTarget: function(event){
return event.target||event.srcElement;
},
preventDefault: function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
removeHandler: function(element, type, handler){
//
},
stopPropagation: function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
}

事件类型

1、 UI事件
2、 焦点事件
3、 鼠标事件
4、 滚轮事件
5、 文本事件
6、 键盘事件
7、 合成事件
8、 变动事件

UI事件

1、 load事件
第一种:

1
2
3
EventUtil.addHandler(window, 'load', function(){
alert('loaded');
})

第二种:指定onload事件处理程序的方式是为 body 元素添加一个onload特性

1
<body onload="alert('Loaded!')"></body>

2、 unload
3、 abort
4、 error
5、 select
6、 resize

当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。这个事件在window上面触发,因此可以通过Javascript或者body元素中的onresize特性来指定事件处理程序。推荐方式:

1
2
3
EventUitl.addHandler(window, "resize", function(event)){
alert('resized');
}

7、 scroll

在混杂模式下,可以通过body元素的scrollLeft和scrollTop来监控这一变化,在标准模式下,通过html元素来反应这一变化。

1
2
3
4
5
6
7
EventUitl.addHandler(window, 'scroll', function(event){
if(document.compatMode =='CSS1Compat'){
alert(document.documentElement.scrollTop);
}else{
alert(document.body.scrollTop);
}
})

鼠标与滚轮事件

DOM3级事件中定义了9个鼠标事件:

1、 click事件
2、 dbclick事件
3、 mousedown事件:用户按下鼠标任意按钮时触发
4、 mouseenter: 在鼠标光标从元素外部首次移动到元素范围之内时触发,不冒泡,且在光标移动到后代元素上不会触发。
5、 moouseleave: 在位于元素上方的鼠标光标移动到元素范围之外时触发,不冒泡,且在光标移动到后代元素上不会触发。
6、 mousemove: 当鼠标指针在元素内部移动时重复地触发。
7、 mouseout: 在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。
8、 mouseover: 在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。
9、 mouseup: 用户释放鼠标按钮时触发。

页面上所有元素都支持鼠标事件。除了mouseenter和mouseleave,所有鼠标事件都会冒泡,也可以取消,而取消鼠标事件将会影响浏览器的默认行为。

只有在同一元素上相继触发mousedown和mouseup事件,才会触发click事件;如果mousedow或mouseup中的一个被取消,就不会触发click事件。

客户区坐标位置

1
2
3
4
5
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
alert("Client coordinates: " + event.clientX + "," + event.clientY);
})

页面坐标位置

1
2
3
4
5
6
7
8
9
10
11
12
var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
var pageX = event.pageX,
pageY = event.pageY;
if(pageX == undefined){
pageX = event.clientX + (document.body.scrollLeft||document.documentElement.scrollLeft);
}
if(pageY == undefined){
pageY = event.clientY + (document.body.scrollTop||docuemnt.docuemnetElement.scrollTop)''
}
})

屏幕坐标位置

1
2
3
4
5
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
alert("Screen coordinates: " + event.screenX + "," + event.screenY);
})

修改键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var div = document.getElementById('myDiv');
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
var keys = new Array();
if(event.shiftKey){
keys.push("shift");
}
if(event.ctrlKey){
keys.push("ctrl");
}
if(event.altKey){
keys.push('alt');
}
if(event.metaKey){
keys.push('meta')
}
alert('Keys:' + keys.join(','));
})

键盘与文本事件

3个键盘事件:
1、 keydown: 用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件
2、 keypress: 用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件
3、 keyup: 当用户释放键盘上的键时触发

文本事件:textInput