事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。
观察者模式的模型
事件流
在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。
事件流描述的是从页面中接收事件的顺序。
IE 的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事件捕获流。
事件冒泡
IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
由小到大,由子到父 div -> body -> html -> document
事件捕获
事件捕获的思想 是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在 事件到达预定目标之前捕获它。
由父到子到孙… document -> html -> body -> div
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:
- 事件捕获阶段——为截获事件提供了机会
- 处于目标阶段——实际的目标接收到事件
- 事件冒泡阶段——对事件做出响应
事件处理程序
事件就是用户或浏览器自身执行的某种动作
事件有事件名
事件处理程序(或事件侦听器):响应某个事件的函数
为事件指定处理程序的方式:
HTML事件处理程序
<input type="button" value="Click Me" onclick="alert('Clicked')" />
缺点:
- 存在一个时差问题,尚未加载就使用
- 扩展事件处理程序的作用域链在不同浏览器中会导致不同结果
很多 HTML 事件处理程序都会被封 装在一个 try-catch 块中,以便错误不会浮出水面; 不同 JavaScript 引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
- HTML 与 JavaScript 代码紧密耦合
使用 JavaScript 指定事件处理程序
- 必须取得一个要操作的对象的引用
- 将事件处理程序属性的值设置为一个函数
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
};
btn.onclick = null; //删除事件处理程序
DOM2 级事件处理程序
“DOM2 级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作 addEventListener() 和 removeEventListener()
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
3 个参数:
- 要处理的事件名
- 作为事件处理程序的函数
- 一个布尔值
最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;
如果是 false,表示在冒泡阶段调用事件处理程序。
IE事件处理程序
attachEvent() 和 detachEvent()
2个参数:
- 事件处理程序名称
- 事件处理程序函数
在 IE 中使用 attachEvent()与使用 DOM0 级方法的主要区别在于事件处理程序的作用域。 在使用 DOM0 级方法的情况下,事件处理程序会在其所属元素的作用域内运行; 在使用 attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
// 移除
btn.detachEvent("onclick", handler);
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;
}
}
};
事件对象
包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。
事件对象的属性与方法
bubbles——表明事件是否冒泡
cancelable——表明是否可以取消事件的默认行为
currentTarget——其事件处理程序当前正在处理事件的那个元素
defaultPrevented——为 true 表 示 已 经 调 用 了 preventDefault()
detail——与事件相关的细节信息
eventPhase)调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段
preventDefault()——取消事件的默认行为。如果cancelable是true,则可以使用这个方法
stopImmediatePropagation()——取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用
stopPropagation()——取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法
target——事件的目标
type——被触发的事件的类型
在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则 this、currentTarget 和 target 包含相同的值。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this);
alert(event.target === this);
};
在需要通过一个函数处理多个事件时,可以使用 type 属性
var btn = document.getElementById("myBtn");
var handler = function(event){
switch(event.type){
case "click":
alert("Clicked");
break;
case "mouseover":
event.target.style.backgroundColor = "red";
break;
case "mouseout":
event.target.style.backgroundColor = "";
break;
}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
阻止特定事件的默认行为
event.preventDefault();
取消进一步的事件 捕获或冒泡
event.stopPropagation();
事件对象的eventPhase属性
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.eventPhase); //2
};
document.body.addEventListener("click", function(event){
alert(event.eventPhase); //1
}, true);
document.body.onclick = function(event){
alert(event.eventPhase); //3
};
只有在事件处理程序执行期间,event 对象才会存在;一旦事件处理程序执行完 成,event 对象就会被销毁。
跨浏览器的事件对象
var EventUtil = {
addHandler: function(element, type, handler){
//省略的代码
},
// 返回event对象的引用
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;
}
}
};
事件类型
- UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发;
- load事件,页面完全加载后触发
- unload 事件,这个事件在文档被完全卸载后触发
- resize事件,当浏览器窗口被调整到一个新的高度或宽度时,就会触发 resize 事件
- scroll事件,页面中相应元素的变化
- 焦点事件,当元素获得或失去焦点时触发;
- blur事件,失焦触发
- focus事件,获得焦点时触发
- 鼠标事件,当用户通过鼠标在页面上执行操作时触发;
- click
- dbclick
- mousedown——按下
- mouseenter——进入范围内
- mouseleave——离开范围
- mousemove——在元素内部移动
- mouseout
- mouseover
- mouseup——释放鼠标按钮
- 滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
- 文本事件,当在文档中输入文本时触发;
- keydown
- keypress
- keyup
- 键盘事件,当用户通过键盘在页面上执行操作时触发;
- 合成事件,当为 IME(Input Method Editor,输入法编辑器)输入字符时触发;
- 变动(mutation)事件,当底层 DOM 结构发生变化时触发。
EventUtil.addHandler(window, "scroll", function(event){
if (document.compatMode == "CSS1Compat"){
alert(document.documentElement.scrollTop);
} else {
alert(document.body.scrollTop);
}
});
HTML5事件
contextmenu事件
何时应该显示上下文菜单
beforeunload事件
在页面卸载前阻止这一操作
DOMContentLoaded事件
形成完整的 DOM 树之后就会触发
readystatechange事件
提供与文档或元素的加载状态有关的信息
readyState: uninitialized/loading/loaded/interactive/complete
EventUtil.addHandler(document, "readystatechange", function(event){
if (document.readyState == "interactive"){
alert("Content loaded");
} });
pageshow 和 pagehide 事件
在页面显示时触发/卸载页面的时候触发
hashchange事件
HTML5 新增了 hashchange 事件,以便在 URL 的参数列表(及 URL 中“#”号后面的所有字符串) 发生变化时通知开发人员。之所以新增这个事件,是因为在 Ajax 应用中,开发人员经常要利用 URL 参 数列表来保存状态或导航信息。
触摸与手势事件
touchstart——当手指触摸屏幕时触发
touchmove——滑动时连续触发
touchend——移开手指时触发
touchcancel——停止跟踪触摸时触发
内存与性能
在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。
首先,每个函数都是对象,都会占用内存,内存中的对象越多,性能就越差。
其次,必须事先指定所有事件处理程序而导致的 DOM 访问次数,会延迟整个页面的交互就绪时间。事实上,从如何利用好事件处理程序的角度出发,还是有一些方法能够提升性能的。
事件委托
事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
使用事件委托,只需在 DOM 树中尽量最高的层次上添加一个事件处理程序。
事件目标是被单击的列表项,故而可以通过检测 id 属性来决定采取适当的操作。
如果你知道某个元素即将被移除,那么最好手工移除事件处理程序。