1、事件流
1.1 事件冒泡
IE8- 浏览器支持的事件流是事件冒泡。事件冒泡是事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)的过程。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="myDiv">Click me </div>
</body>
</html>
在上面的HTML页面中,如果点击了页面中的 div#myDiv ,那么事件就会按照如下顺序冒泡:div --> body --> html -->document。也就是说,click 事件在这些元素上都会发生。
所有现代浏览器都支持事件冒泡,IE9+、Firefox、Chrome、Safari 会将事件一直冒泡到 window 对象。
1.2 事件捕获
事件捕获与事件冒泡过程相反。事件捕获是由不太具体的节点先接收到事件,而最具体的节点最后接收到事件,由上而下触发事件。
以上面的HTML页面为例,如果点击了页面中的 div#myDiv ,那么事件就会按照如下顺序捕获触发:document --> html --> body -->div。
IE9+、Firefox、Chrome、Safari 都支持事件捕获,并且是由 window 对象开始捕获事件的。
1.3 DOM事件流
“DOM2级事件”规定事件流包括三个阶段,其顺序是:事件捕获阶段、处于目标阶段、事件冒泡阶段。
“DOM2级事件”规定要求捕获阶段不会涉及事件目标,但 IE9+、Firefox、Chrome、Safari、Opera9.5+ 都会在捕获阶段和冒泡阶段触发目标对象上的事件,即有两此处理目标对象事件的机会。IE8- 不支持DOM事件流。
2、事件处理程序
事件是用户或者浏览器自身执行的某个动作,诸如,click、load、mouseover等。事件处理程序(事件侦听器)是响应事件的函数,以“on“ 开头。为事件指定处理程序的方式有多种。
2.1 HTML 事件处理
每个元素值得每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值是能够执行的 JavaScript 代码。且这个值不能使用未经转义的HTML语法字符,如:&、”“、<、>等,如果要使用这些字符,需要使用转义值。
这样指定事件处理程序会创建一个封装着元素属性值的函数,这个函数有一个event变量指向事件对象,而且在这个函数内部,this 指向目标元素。这个函数扩展了它的作用域,可以访问document对象以及该元素本身成员。如果当前元素是一个表单元素,则作用域还会包含表单元
<button onclick="alert('"clicked"')">点我</button> <!--在此不能直接使用双引号("") -->
<button onclick="showMessage()">点我</button> <!--可以访问全局作用域中的函数 -->
<button value="hello" onclick="alert(this.value +' '+event.type)">点我</button> <!--暗中包含 event事件对象 和 this(目标元素) -->
<button id="helloid" onclick="alert(this.id +' '+event.type)">点我</button> <!--可以 this.attr 访问元素的属性 -->
<button value="hello" onclick="alert(value +' '+event.type)">点我</button> <!--直接访问元素的属性,button在IE7-中 js访问的value值是button的文本值,其他浏览器则是value特性的值-->
<button value="hello" onclick="showMessage(event,this)">点我</button> <!--传递参数 event 和 this -->
<input type="button" value="hello" onclick="showMessage(event,this)"/> <!-- -->
<form action="">
<input type="text" name="username" value="">
<input type="button" value="echo UserName" onclick="alert(username.value)"> <!--直接访问form表单的其他表单元素 -->
</form>
function showMessage(event,element){
console.log(event.type);
console.log(element.value);
}
所有现代浏览器都支持这种方式添加事件处理程序。
2.2 DOM0 级事件处理程序
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性,会覆盖HTML特性指定的事件处理程序。
每个元素(包括 window 和 document)都有自己的事件处理程序属性,通常是小写的,如 onclick。
使用DOM0 级方法指定的事件处理程序被认为是元素的方法,所以事件处理程序是在元素的作用域中运行的,即程序中 this 指向当前元素。DOM0的事件处理程序是在事件冒泡阶段被处理的。
var btn = document.getElementById("myBtn");
btn.onclick = function(){ // 覆盖HTML 特性指定的事件处理程序
alert(this.id); // this 指向btn
} btn.onclick = null; // 删除事件处理程序
所有现代浏览器都支持这种方式。
2.3 DOM2 级事件处理程序
“DOM2 级事件”定义了两个方法,用于增加、删除事件处理程序的操作,addEventListener(eventType, handler, notBubble),removeEventListener(eventType, handler, notBubble),所有节点都包含这两个方法。
eventType:事件类型,"click"、"load"等;handler:事件处理函数;notBubble:是否在冒泡阶段处理,true(捕获阶段调用事件处理程序),false(冒泡阶段处理事件)。
使用 addEventListener() 可以添加多个事件处理程序,并按照添加的顺序执行,添加的事件处理程序是在元素的作用域中运行的,即程序中 this 指向当前元素。removeEventListener()不能移除用匿名函数添加的事件处理程序
function showMessage(){
console.log("third event process");
} var btn = document.getElementById("mybtn");
var btn2 = document.getElementById("mybtn2");
var btn3 = document.getElementById("mybtn3"); btn.addEventListener("click",function(){ // 添加匿名处理函数,不能被移除; 第二个执行
console.log("another event process");
console.log(this.id); // this 指向当前元素
},false); btn.onclick = function(){ //第一个执行 console.log(this.id);
} btn.addEventListener("click",showMessage,false); // 添加命名处理函数,可以被移除;第三个执行 btn2.onclick = function () {
btn.onclick = null; // 删除事件处理程序,使用addEventListener()添加的处理程序仍然存在
}
btn3.onclick = function () {
btn.removeEventListener("click",showMessage,false); // 删除事件处理程序
}
Chrome、Firefox、Safari、Opera、IE9+ 支持 addEventListener(),removeEventListener()
2.4 IE 事件处理程序
IE10- 实现了 attachEvent(eventType,handler),detachEvent(eventType,handler),来添加、去除事件处理程序,其中evenType是包含 "on"的,如"onclick"。IE11不支持。
因为IE8- 只支持事件冒泡,所以使用 attachEvent() 添加事件处理程序,会添加到事件冒泡阶段,并且事件处理函数会在全局作用域中执行,即 this 指向 widow 对象。也可添加多个事件处理程序,但IE8-按照以与添加顺序相反的顺序执行事件处理函数,而IE9、IE10 按照正序执行。
function showMessage(){
console.log("third event process");
} var btn = document.getElementById("mybtn");
var btn2 = document.getElementById("mybtn2");
var btn3 = document.getElementById("mybtn3"); btn.attachEvent("onclick",function(){ // IE8- 第三个执行,IE9、IE10第二个执行
console.log("another event process");
console.log(this == window); //true,事件添加 this 指向 window 对象
});
btn.onclick = function(){ // 第一个执行
console.log(this.id);
}
btn.attachEvent("onclick",showMessage); // IE8- 第二个执行,IE9、IE10第三个执行 btn2.onclick = function () { btn.onclick = null; // 删除事件处理程序
}
btn3.onclick = function () { btn.detachEvent("onclick",showMessage); // 删除事件处理程序
}
注意:在一个事件有多个处理程序时,如果其中一个处理程序某处发生错误了,只会影响该处理程序的后面的代码,并不影响其他的事件处理程序的执行,以及事件传播,不管这些事件程序是以什么方法添加的。
3、事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,包含着所有与事件有关的信息。只有在事件处理程序执行期间,event 对象才会存在,事件处理程序执行完后,event对象就会被销毁。
3.1 DOM中的事件
兼容DOM事件(包含DOM0和DOM2)的浏览器会将一个 event 对象传入到事件处理程序中。以HTML特性添加的事件处理程序也存在 event 对象,并且和DOM事件对象具有一样的操作。
DOM事件对象的属性和方法:
属性/方法 | 类型 | 读/写 | 说明 |
bubbles | Boolean | 只读 | 表明事件是否冒泡 |
cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 |
currentTarget | Element | 只读 | 事件处理程序当前正在处理事件的那个元素,等于 this 对象 |
defaultPrevented | Boolean | 只读 | 为true表示已经调用了preventDefault() |
detail | Integer | 只读 | 与事件相关的细节信息 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段,1表示捕获阶段,2表示处于目标阶段,3表示冒泡阶段 |
preventDefault() | Function | 只读 | 取消事件的默认行为,如果cancelable是true,则可以使用这个方法 |
stopImmediatePropagation() | Function | 只读 | 取消时间进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3级) |
stopPropagation() | Function | 只读 | 取消事件进一步捕获或冒泡。如果bubbles为true,则可使用这个方法 |
target | Element | 只读 | 事件的目标元素 |
trusted | Boolean | 只读 | 为true表示事件是浏览器生成的,为false,表示事件是有开发者通过JavaScript创建的(DOM3级) |
type | String | 只读 | 事件的类型 |
view | AbstructView | 只读 | 与事件关联的抽象视图,等同于发生事件的window对象 |
var btn = document.getElementById("mybtn");
var btn2 = document.getElementById("mybtn2");
var link = document.getElementById("myLink"); btn.onclick = function(event){
console.log(event.type); // "click"
console.log(event.currentTarget == this); //true
console.log(event.target == this); // true
console.log(event.eventPhase); //
}
btn.addEventListener("click",function (event) {
console.log(event.type); // "click"
console.log(event.eventPhase); //
},false); document.body.onclick = function(event){ // 事件冒泡到body console.log(event.currentTarget == document.body); // true
console.log(this == document.body); // true
console.log("body-bubbles:"+event.target.id); //
console.log(event.eventPhase); //
} document.body.addEventListener("click",function(event){
console.log("body-capture: "+event.type +",target: "+ event.target.id);
//event.stopPropagation(); // 如果在捕获阶段阻止事件传播,目标元素定义的事件处理程序就不会执行了
console.log(event.eventPhase); //
},true); var handler = function(event){
switch(event.type){
case "click":
console.log("clicked");break;
case "mouseover":
event.target.style.backgroundColor = "red";break;
case "mouseout":
event.target.style.backgroundColor = "";break;
}
} btn2.onclick = handler;
btn2.onmouseover = handler;
btn2.onmouseout = handler; // <a href="https://www.baidu.com" target="_blank" id="myLink" onclick="event.preventDefault();">baidu</a>
link.onclick = function(event){
event.preventDefault();
}
3.2 IE中的事件对象
1)DOM0级添加的事件处理程序,事件对象是作为 window 对象的一个属性(event)而存在。在IE9+中事件对象对象也会作为参数传入事件处理程序函数(其实是IE9+开始支持DOM事件对象)。
2)attachEvent() 添加的,事件对象对象作为参数传入事件处理程序函数,同时也可以通过 window.event 得到事件对象
3)通过 HTML 指定的事件处理程序,也可以通过 event 变量访问事件对象(与DOM中一样)
IE事件对象的属性和方法:
属性/方法 | 类型 | 读/写 | 说明 |
cancelBubble | Boolean | 读/写 | 默认值为false,将其设置为true就取消事件传播 |
returnValue | Boolean | 读/写 | 默认值为true,设置为false取消事件的默认行为 |
scrElement | Element | 只读 | 事件的目标元素 |
type | String | 只读 | 事件类型 |
var btn = document.getElementById("mybtn");
var link = document.getElementById("myLink");
btn.attachEvent("onclick",function(event){
console.log(this == window); //true
console.log(event.srcElement.tagName); //
console.log(window.event.srcElement.tagName); // event 和 window.event 是不相等的
}); btn.onclick = function(event){
console.log(this == window.event.srcElement); // true
事件对象
console.log(event); // 在IE8- 中, 参数 event 的值是undefined
console.log(window.event.type); //click, 所有IE 都支持,新版本的Chrome、edge也支持,Firefox不支持
console.log(event == window.event); // false, IE9+ 开始支持DOM 事件对象,但参数event 和 原本 window.event 不是同一个事件对象 event.stopPropagation(); // 在IE9+ 中能运行。IE8-报错 event = window.event;
event.cancelBubble = true; // 阻止事件传播 } document.body.onclick = function(){ // 事件冒泡到body console.log("body clicked");
} link.onclick = function(event){ //在IE8- 中, 参数 event 的值是undefined
var event = window.event;
event.returnValue = false; // 阻止默认行为
}
4、事件类型