事件:就是一种浏览器通知,告诉当前窗口在文档中要进行哪种交互。文档中的很多交互需要有前置条件,只有满足这些前置条件时,才会去触发执行某个动作。而正是事件实现了这种交互。
事件类型:
事件类型也就是事件名称,以字符串的形式存在,用于指定要发生哪种事件。常用的事件类型:
表单:focus、blur、sumbit、change等
鼠标:mouseover、mouseup、mousemove、mousedown、click、dbclick等
键盘:keydown、keyup、keypress等
触屏:touchstart、dragenter、dragend等
拖放:dragstart、dragenter、dragend等
文档加载:load、DOMContentLoaded等
窗口:resize、scroll、load、unload等
事件目标:
事件目标就是注册了某个事件的对象,如果要执行事件,就必须指定事件类型和事件目标。在JavaScript中,最常用的事件目标就是Window、Document和Element。
事件处理程序:
事件处理程序就是用户或浏览器自身执行的某种动作。例如click、load等,都是事件的名称,而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以"on"开头,因此click事件的时间处理程序就是onclick,load事件的时间处理程序就是onload。
为事件指定处理程序的方式或注册事件(绑定事件)的方式:
1)HTML事件处理程序
当在HTML属性中注册事件处理程序时,不能包含函数声明,也就是不能包含大括号和function关键字,只能是函数调用或具体的代码块:
<button type="button" onclick="console.log("onclick")">提交</button>
<!--不能下面的函数声明-->
<button type="button" onclick="function(event){}">提交</button>
此时的this会根据不同的情况,指向不同的对象。如果是在事件处理程序中执行代码块,那么此时指向的是当前元素对象;如果是在事件处理程序中调用函数,那么此时指向的是全局对象。
<button type="button" onclick="showThis()">btn</button>
<script>
function showThis(){
console.log(this);//window
}
</script>
在这种方式中的事件处理程序允许使用作用域链的快捷方式,也就是在调用对象的方法时,不用显式地将对象写出来。
<button type="button" onclick="console.log(type)">btn</button>
如果想阻止事件的默认行为操作,只要将事件处理程序的返回值设为false即可。
<button type="button" onclick="return false;">btn</button>
这种注册方式实现起来很简单,但是有很大的局限性。首先不能控制注册事件的时机,也就是说某些事件需要满足一些条件后才能注册,而这种方式只要文档渲染好后,执行某个动作(如点击按钮)就能触发对应的事件,其次,HTML和JavaScript代码紧密耦合,冗长的代码串即不利于维护,也不符合结构与行为分离的规则。
2)DOM0级事件处理程序(定义对象属性)
将一个函数(事件处理程序)赋给对象的一个属性,此类属性可称为事件处理程序属性。根据规定,属性的名字要以“on”开头,后面跟随事件类型,并且必须小写,即使是多个单词也要小写,例如onclick、onload等。注意在IE8及以下的版本获取事件对象要调用全局属性event;而其他浏览器中,事件处理程序都会包含一个事件对象的参数。
<button id="btn" type="button">btn</button>
<script>
var myBtn=document.getElementById("btn");
myBtn.onclick=function(event){
var event=event||window.event;
console.log(this);
}
</script>
此时的this指向的是正在处理当前事件的对象,也就是元素。如果要移除事件的绑定,也很简单,只要将null赋给此属性即可。
myBtn.onclick=null
当时间处理程序的返回值是false时,就能阻止事件的默认操作。例如表单提交按钮在点击时会刷新窗口,如果返回false,就不会有这个行为。
这种方法的缺点是同一类型的事件,只能注册一个,如果注册多个,那么后面的将把前面的覆盖。
myBtn.onclick=function(event){
console.log("btn1");
}
myBtn.onclick=function(event){
console.log("btn2");
}
//只会执行第二个
3)DOM2级事件处理程序
除IE8及以下版本外,所有的浏览器都支持addEventListener()方法。此方法由事件目标调用,能接受三个参数,第一个是事件类型(不包含前缀“on”),第二个参数是事件处理程序,第三个参数是一个布尔值,表示捕获(true)还是冒泡(false)。
var myBtn=document.getElementById("btn");
myBtn.addEventListener("click",function(){
console.log(this);
},false);
此方法中的事件处理程序包含一个参数:事件对象(event),并且此时this指向的是正在处理该事件的对象,其值始终与事件对象的currentTarget属性相同。当用这个方法注册同一类型的事件时,事件之间不会出现覆盖,而是会依次执行。
myBtn.addEventListener("click",function(){
console.log("btn1");
},false);
myBtn.addEventListener("click",function(){
console.log("btn2");
},false);
//btn1 btn2
通过这种方式注册的事件,不能通过返回值来阻止其默认行为,需要用事件对象的一个方法,这个方法就是preventDefault();
如果需要移除事件,就需要用对应的移除方法removeEventListener(),此方法也有三个参数和addEventListener()方法中的第二个参数相同,否则不能将事件移除。
myBtn.addEventListener("click",function(){
console.log("btn1");
},false);
myBtn.removeEventListener("click",function(){
console.log("btn1");
},false);
//这样事件是无法移除的,因为这是两个不同的函数
function remove(){
console.log("btn1");
}
myBtn.addEventListener("click",remove,false);
myBtn.removeEventListener("click",remove,false);
4)IE事件处理程序
IE实现了与DOM中两个类似的方法:attachEvent()和detachEvent(),这两个方法接收相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8及更早的版本只能支持事件冒泡,所以通过attachEvent()添加的事件处理程序就会被添加到冒泡阶段。事件处理程序会在全局作用域中执行,此时的this会指向全局对象。
var myBtn=document.getElementById("btn");
myBtn.attachEvent("onclick",function(){
console.log(this);//window
});
此方法同样不能用返回值来取消事件,需要用事件对象的一个属性returnValue。当属性被设置为false时,就能阻止事件的默认操作。如果要移除事件,那么可以用对应的移除detachEvent()。它也接收两个参数,含义也与attachEvent()中的相同,并且两个事件处理程序要相同才能移除成功。
通过设置对象属性或HTML属性注册的事件总是会被优先调用。注意,这两种方式不能并存,对象属性注册的事件会将HTML属性注册的事件覆盖。
<button id="btn" type="button" onclick="console.log("btn1")">btn</button>
<script>
var myBtn=document.getElementById("btn");
myBtn.onclick=function(){
console.log("btn2")
};
myBtn.addEventListener("click",function(){
console.log("btn3")
});
</script>
//btn2 btn3
跨浏览器的事件处理程序
为了以跨浏览器的方式处理事件,可以使用能力检测的方法,来编写保证在大多数浏览器下可以一致运行的代码。
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 myBtn=document.getElementById("btn");
var hander=function(){
console.log("btn");
}
EventUtil.addHandler(myBtn,"click",handler);
//执行其他代码
EventUtil.removeHandler(myBtn,"click",handler);
事件对象:
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。例如,鼠标操作导致的事件对象中,包含鼠标为位置的信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。所有的浏览器都支持event对象,但支持方式不同。
DOM中的事件对象
event对象包含于创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样,不过,所以事件都会有以下的成员:bubbles、cancelable、currentTarget、defaultPrevented、detail、eventPhase、preventDefault、stopImmediatePropagation()、stopPropagation()、target、trusted、type、view。
事件对象常用的方法:
preventDefault:阻止时间的默认操作
stopPropagation():阻止事件传播
stopImmediatePropagation()不但阻止事件的传播,而且还会阻止触发注册在元素上的当前事件类型的其他事件处理程序。
event.target与event.currentTarget的区别:
event.target返回的是触发事件的元素
event.currentTarget返回的是绑定事件的元素
var myBtn=document.getElementById("btn");
//只会输出 btn1 btn2
myBtn.onclick=function(event){
console.log("btn1");
event.stopPropagation();
};
myBtn.addEventListener("click",function(event){
console.log("btn2");
});
myBtn.parentNode.addEventListener("click",function(event){
console.log("div");
});
//只会输出 btn1注册在当前事件类型上的其他事件处理程序也会被阻止触发
myBtn.onclick=function(event){
console.log("btn1");
event.stopImmediatePropagation();
};
IE中的事件对象
IE中的事件对象可以通过全局对象的event属性获得,它也有许多与事件信息相关的属性和方法,有一些属性可与DOM中的事件对象的属性或方法对应
cancelBubble:是否阻止事件冒泡,当设为true时,相当于调用stopPropagation()方法
returnValue:是否可以阻止事件的默认操作,当设为false时,相当于调用preventDefault()方法
srcElement:事件对象,相当于target属性
跨浏览器的事件对象
虽然DOM与IE中的event对象不同,但基于它们之间的相似性依旧可以拿出浏览器的方案来。IE中event对象的全部信息和方法DOM对象中都有,只不过实现的方式不一样。
var EventUtil={
getEvent:function(event){
return event?event:window.event;
},
getTarget:function(event){
return event.target||event.srcElement;
},
preventDefault:function(){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}
btn.onclick=function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
EventUtil.preventDefault(event);
EventUtil.stopPropagation(event);
};
事件传播(事件流):
当点击文档中的一个元素(如按钮)时,元素触发点击事件,同时此元素的容器元素也会触发点击事件,如果容器元素之上还有其他元素,那么也要触发点击事件,以此类推,发生一连串连锁效应,浏览器的这种行为就叫事件传播。事件传播描述了文档中的元素接收事件的顺序,总共有两种事件传播的形式:冒泡和捕获。
标准的事件流有两种形式:冒泡和捕获,除了IE8及以下版本不支持捕获外,其他浏览器对象都支持。
冒泡目标元素会向自己的父级元素传播,逐级传播,然后就会到达body,html,之后传播到Document,最后到Window,停止冒泡。捕获整好相反会从window传播到目标元素。
"DOM2级事件"规定的事件流包括三个阶段:
事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供机会。
然后是实际的目标接收到事件。
最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应。
事件对象中有个eventPhase属性,可以获悉当前处于哪个阶段的传播。它是一个数组,1表示捕获,2表示处于目标上,3表示冒泡。
<div>
<button id="btn" type="button" onclick="console.log("btn1")">btn</button>
</div>
<script>
var myBtn=document.getElementById("btn");
myBtn.addEventListener("click",function(event){
event.eventPhase;//2
},false);
myBtn.parentNode.addEventListener("click",function(event){
event.eventPhase;//3
event.target;//可能是button元素,也可能是div
},false);
</script>
注意:事件对象中的target属性指向的是事件目标,但是发生事件传播时,它指向的可能不是定义时的事件目标。
取消事件:
有些事件存在与之相关的默认操作,例如超链接的重定向。事件处理程序可以通过返回一个适当的值(例如false)或者调用事件对象的某个方法(例如preventDefault())来阻止默认操作的发生,这个操作也叫取消事件。