事件流
IE和Netscape开发团队提出了完全相反的两种事件流的概念,事件冒泡流和事件捕获流。
事件冒泡
事件由最具体的元素开始,逐级向上传播到较不具体的元素,最终到文档。
事件捕获
事件捕获从document开始,逐级向下,最后传到最具体的节点。
DOM事件流
DOM2级事件定义的事件流包含3个阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段。
捕获阶段会从文档节点自上而下传递直到目标节点的上一个节点;处于目标阶段时传到目标节点,冒泡阶段开始向上传递知道文档节点。
规定是捕获阶段事件不传递到目标节点,但是大多数浏览器就传递到了,这就意味着有2次机会在目标对象上操作事件。
事件处理程序
响应某个事件的函数
HTML事件处理程序
可以使用HTML特性来指定。
<input type="button" value="Click Me" onclick="alert(event.type)">
<input type="button" value="Click Me" onclick="alert(this.value)">
<input type="button" value="hahaah" onclick="clickButton(this)">
function clickButton(element) {
alert(element.value);
}
这里注意JS代码中如果出现&”“<>要使用转义字符。
这样写事件处理程序是很不灵活的,HTML代码和JS代码耦合度太高。
不过这样写是有一些方便的地方的,这样写的JS语句会被包含在一个动态创建的函数中,这个函数中会存在一个局部变量event事件对象,而且通过this这个变量可以访问到元素本身。这个函数还会使用with拓展作用域,让你更方便的直接访问document及元素本身的成员:
function(){
with(document){
with(this){
//你的代码
}
}
}
//于是可以直接这样写
<input type="button" value="Click Me" onclick="alert(value)">
如果要调用函数,这个函数在JS中要处于全局作用域哦,而且要使用this对象要将this作为参数传进去,否则是访问不到的。
这样写事件处理程序是有问题的,一个是紧耦合的问题,一个是时间差,如果你的JS文件是放在最下面的,有可能会出现函数已经绑在事件上了可是JS却还没给出函数定义,这样就会报错,为了避免这样的情况出现,我们使用try-catch:
<input type="button" value="Click Me" onclick="try{clickButton(this);}catch(ex){}">
不过还是不那么理想是吧。
DOM0级事件处理程序
每个元素都有自己的事件处理程序属性,以这种方式添加的事件会在事件流的冒泡阶段被处理。传入的处理函数是在元素的作用域中运行。将这个属性指向空就取消了事件绑定,值得一提的是,如果你使用上面的HTML特性指定事件处理函数,这个属性里就包含着HTML里你写的事件函数的代码,置空也同样可以取消绑定。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id); //"myBtn"
};
btn.onclick = null;
DOM2级事件处理程序
IE8及以下不支持DOM2级事件
DOM2级事件定义了两种方法:addEventListener() removeEventListener()。这两个方法接受3个参数,要处理的事件名,处理函数,和一个布尔值。这个布尔值代表在捕获节点调用事件处理程序(true)还是在冒泡过程中调用。
这样添加意味着可以添加多个事件处理程序,在事件触发时会按照添加的顺序来执行。传入的处理函数是在元素的作用域中运行。
注销时要传入完全相同的函数才能注销,这就意味着如果你的处理函数是以匿名函数的方式传递进去的,那就不可能注销了哦,因为再传进去一个匿名函数也不是原先那个了:
var btn = document.getElementById("myButton");
var body = document.body;
//冒泡阶段
body.addEventListener("click", function(){
alert("Hello world!");
}, false);
//捕获阶段
body.addEventListener("click", function(){
alert("Hello world!");
}, true);
//如果是使用匿名函数注册的
btn.addEventListener("click", function(){
alert(this.id + "匿名");
}, false);
//注销不掉
btn.removeEventListener("click", function(){
alert(this.id);
}, false);
//这样就能注销掉了
var handler = function(){
alert(this.id + "非匿名");
};
btn.addEventListener("click", handler, false);
btn.removeEventListener("click", handler, false);
IE事件处理程序
IE8及以下不支持DOM2级事件,但是支持两个类似方法attachEvent()、detachEvent()
只支持冒泡阶段
传入的处理函数是在全局作用域中运行
添加多个事件时触发顺序与添加顺序相反
注销时同样需要传入相同的参数,匿名函数无法注销。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert(this === window); //true
});
var handler = function(){
alert("Clicked");
};
btn.attachEvent("onclick", handler);
btn.detachEvent("onclick", handler);
跨浏览器事件处理程序
优先使用DOM2级的,再不行使用IE专有的,最后使用DOM0级的(一般不可能用到)
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.getElementById("myButton");
var handler = function(){
alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
EventUtil.removeHandler(btn, "click", handler);
事件对象
事件被触发时会产生一个事件对象,这个对象中包含着所有与事件有关的信息,包括导致事件的元素,事件的类型以及其他各个事件特定的信息。所有浏览器都支持event对象,但是实现不同。
DOM中的事件对象
兼容DOM的浏览器会将一个event对像传入到事件处理程序中,三种方式都有
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
alert(event.type); //"click"
}, false);
<input type="button" value="Click Me" onclick="alert(event.type)"/>
所有的event对象都会有下面的方法:
属性/方法 | 类型 | 读写 | 说明 |
---|---|---|---|
bubbles | Boolean | 只读 | 事件是否冒泡 |
cancelable | Boolean | 只读 | 是否可以取消 |