第一次在segmentfault写文章,希望通过这种方式来巩固所学的知识,也欢迎童鞋们指正其中有不对和错误的地方。^+^
事件流
事件流:页面中接收事件的顺序,即当一个事件发生时,该事件的传播过程便叫做事件流
事件流的种类
事件冒泡
事件由最具体的元素开始逐级向上传播至较为不具体的节点(文档)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="box">点我</div>
</body>
</html>
当我们点击id为box的div时,该点击事件传播顺序如下
div --> body --> html -->document
特别说明:IE5.5及更早的版本将绕过html节点直接到document,IE9,Firefox,chrome和safari将冒泡到window对象
事件捕获
事件捕获和事件冒泡似乎截然相反,由不太具体的节点先接收到事件 -->再到最具体的节点。同样还是用上面冒泡例子,则事件的传播顺序则是:
document --> html -->body -->div
特别说明:ie8之前不支持事件捕获,IE9,safari,chrome,opera,firefox目前支持良好。并且这些浏览器不是从document开始捕获,而是从window对象开始。
DOM事件流阶段
捕获阶段
目标阶段
冒泡阶段
以上面的代码为例子,由图可以很清晰地看出首先发生的是事件捕获-->实际的目标接收事件-->事件冒泡
特别说明:在DOM事件流中,实际的目标不会在捕获阶段接收到事件,即捕获阶段到body就停止,"下一阶段"是目标阶段,该阶段可以看成是事件冒泡的一部分,最终事件又被传播会document。
BUT :我们的各大浏览器总是不喜欢按照规范来,IE9,Safari,chrome,firefox及其更高的版本中都会在捕获阶段出发事件对象上的事件,最后导致有两个机会在目标对象上操作事件。
-
事件处理程序
事件:用户或者浏览器自身执行的某个动作,比如load,click,mousemove等
事件处理程序:相应某个事件的函数叫做事件处理函数(也叫做事件侦听器)
事件处理程序类别
1 html事件处理程序:某个元素支持的某个事件可以用与事件处理程序同名的html特性来指定,该特性的值是能够执行的javascript代码。
<input type="button" value="点击" onclick="alert('我被点击了')" />
/*
当点击该按钮的时候,浏览器会弹出'我被点击了';
*/
当然也可以给onclick赋值页面中其他地方定义的脚本
<script>
function show(){
alert('我被点击了');
}
/*
点击后也会弹出 '我被点击了'
*/
</script>
<input type="button" value="点击" onclick="show()" />
优点:简单明了,省去获取元素等一系列前提操作
缺点:html代码与js代码高度耦合,不符合分离原则
2 DOM0级别事件处理函数:使用 element.on[eventname]=fn的方式给元素添加事件
<input type="button" value="点击" id="click" />
<script>
var oBtn=document.getElementById('click');
//该方式被认为是元素的方法,即事件处理程序在元素的作用域中进行,this即该元素本身
oBtn.onclick=function(){
alert(this.id);//click
}
//注意:删除该事件处理程序可以用如下方法
oBtn.onclick=null;//即点击后不再有任何反应
</script>
3 DOM2级事件处理程序:DOM2级添加了addEventListener(添加事件处理程序)和removeEventListener(移除事件处理程序)
添加事件处理函数
<input type="button" value="点击" id="click" />
<script>
var oBtn=document.getElementById('click');
/* 参数1 指定事件名称...click mouseover mouseout
参数2 事件处理程序(匿名函数或者有名函数)
参数3 true(捕获阶段发生) or false(冒泡阶段发生)
*/
oBtn.addEventListener('click',function(){
alert(this.id)//click this指的是该元素作用域内
},false)
//注意该种方式可以给一个函数添加多个事件处理函数,执行顺序与添加顺序相同
oBtn.addEventListener('click',function(){
alert('Hello World')//click
},false)
</script>
移除事件处理函数:如果事件处理函数是有名函数,则可以通过名字来移除,匿名函数无法移除。
<input type="button" value="点击" id="click" />
<script>
var oBtn=document.getElementById('click');
function showId(){
alert(this.id);
};
function HellowWorld(){
alert('HellowWorld');
}
oBtn.removeEventListener('click',showId,false)
//最后只能弹出HellowWorld
</script>
4IE事件处理程序:ie实现了与dom类似的两个方法,attachEvent(添加),detachEvent(删除)
添加事件处理函数
<input type="button" value="点击" id="click" />
<script>
var oBtn=document.getElementById('click');
function showId(){
alert(this.id);
};
oBtn.attachEvent('onclick',showId);//这时候会报错,因为这里的是在window
的作用域内
//修改如下
oBtn.attachEvent('onclick',function(){
showId.call(this);//改变this指向
})
</script>
删除事件处理函数
<input type="button" value="点击" id="click" />
<script>
var oBtn=document.getElementById('click');
function showId(){
alert('HelloWorld');
};
oBtn.attachEvent('onclick',showId);//这时候会报错,因为这里的是在window
的作用域内
//修改如下
oBtn.detachEvent('onclick',showId) ;//点击没有任何反应
</script>
事件函数封装
绑定 为了解决attachEvent的this指向问题,并且可以通过有名称的函数来解除事件绑定,现在处理如下
//
function bind(obj,eventName,fn){
var _fn=fn;
fn=function(){
_fn.call(obj);//改变this指向
};
if(obj.addEventListener){
obj.addEventListener(eventName,fn,false);
}else{
obj.attachEvent('on'+eventName,fn);
}
return fn;//用于事件解除
}
解除
function unbind(obj,eventName,fn){
if(obj.removeEventListener){
obj.removeEventListener(eventName,fn);
}else{
obj.detachEvent('on'+eventName,fn);
}
}
使用方式
//给input添加和移除事件
<input type="button" id="box" value="事件封装" />
//添加
function show( ){
alert(this);
}
function show2( ){
alert(this.id);
}
var removeFn=bind('box','click',show);//需要移除的事件处理程序,不是原程序名称show
bind('box','click',show2);
unbind('box','click',removeFn);
//最后只会弹出 box
事件对象
当触发DOM上面的某个事件的时候,会产生一个事件对象event,这个对象中包含着所有与事件对象有关的信息。例如该事件类型,导致事件的元素等
DOM中的事件对象
DOM中的事件对象:兼容DOM的浏览器会将event对象传入到事件处理程序中,无论指定事件处理程序用什么方式(html方式,DOM0级方式,DOM2级方式)
<input type="button" id="box" value="DOM中的事件对象" onclick="console.log(event.type)">
//html方法 click
var oBox=document.getElementById('box');
//DOM0级别方法 click
oBox.onclick=function(ev){
console.log(ev.type);//click
}
///DOM2级别方法 click
oBox.addEventListener('click',function(ev){
console.log(ev.type);//click
})
总的来说event对象包含与创建他的特定事件有关的属性和方法,但是触发的事件类型不同,则可用的属性和方法也不一样。但是都会包含以下成员
属性/方法 | 类型 | 读/写 | 说明 | |
---|---|---|---|---|
currentTarget | element | 只读 | 事件处理程序当前正在处理程序的那个元素,我的理解是事件的直接绑定者 | |
target | element | 只读 | 事件的目标 | |
cancelable | boolean | 只读 | 表示是否可以取消事件的默认行为 | |
preventDefault() | function | 只读 | 取消事件的默认行为 ,前提是cancelable为true | |
bubbles | boolean | 只读 | 表明事件是否可以冒泡 | |
stopPropagation() | function | 只读 | 取消事件的进一步冒泡或者捕获,前提是bubbles为true | |
type | boolean | 只读 | 事件类型 | |
view | abstractView | 只读 | 与事件关联的抽象视图,等同于发生事件的window对象 | |
detail | integer | 只读 | 与事件相关的细节信息 | |
eventPhase | integer | 只读 | 调用事件处理程序的阶段,1::捕获,2:“处于目标”,3:冒泡 | |
trusted | boolean | 只读 | 为true表示事件是由浏览器生成的,为false表示事件是由开发人员通过js生成的。(DOM3) | |
stopImmediatePropagation() | function | 只读 | 取消事件的进一步捕获或者冒泡,同时阻止任何事件处理程序被调用(DOM3) |
特别说明:只有在事件处理程序被执行的期间,event对象才会存在,一旦事件处理程序执行完成,其就会被销毁。
IE中的事件对象
与访问DOM中的事件对象不同,要访问IE中的event对象有几种不同的方式。取决于指定事件处理程序的方法。
html event
函数参数
window.event
同样IE中的event对象也包含着与创建他的事件相关的属性和方法,其中很多的属性和方法都有对应的或者是相关的DOM属性和方法。当然也会事件的不同,其属性和方法也会有所不同,但是都会包含下表内容
属性/方法 | 类型 | 读/写 | 说明 | |
---|---|---|---|---|
srcElement | element | 只读 | 事件的目标(与DOM中的target属性相同) | |
type | string | 只读 | 事件的类型 | |
cancelBubble | boolean | 只读 | 默认为false,设置为true表示取消冒泡(与stopPropagation()作用相同) | |
returnValue | boolean | 只读 | 默认为true,设置为false就可以取消默认行为(与preventDefault()作用相同) |
跨浏览器事件对象封装
我们为eventUtil添加几个方法,以此来达到有关event对象的常用的跨浏览器的使用目标
getEvent() 获取事件对象
getTarget()获取事件源
stopPropagation() 取消冒泡
preventDefault() 阻止默认行为
var eventUtil={
getEvent:function(ev){
return ev || window.event;//获取事件对象
},
getTarget:function(ev){
return ev.target || ev.srcElement;//获取事件源
},
stopPropagation:function(ev){//阻止冒泡
if(ev.stopPropagation){
ev.stopPropagation();
}else{
ev.cancelBubble=true;
}
},
preventDefault:function(ev){//阻止默认行为
if(ev.preventDefault){
ev.preventDefault();
}else{
ev.returnValue=true;
}
}
}
常见应用之事件委托
说明:需要给页面中成百上千个li绑定一个事件并且输出当前元素的innerHTML
常见做法
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
var aLi //假设已经获取了这组li元素
for(var i=0;i<aLi.length;i++){
aLi[i].onclick=function(ev){
console.log(this.innerHTML);
}
}
这种方式通过遍历DOM节点的方式添加事件处理程序有诸多缺点,比如性能大大减低,新添加的li不具备click事件等。
利用事件委托(冒泡原理)
var oUl//假设oUl是li的父节点
oUL.onclick=fuction(ev){
var ev=eventUtil.getEvent(ev);
var target=eventUtil.getTarget(ev);
console.log(target.innerHTML);
}
利用事件委托可以大大地提高性能,后面随时添加的元素都可以拥有这个点击事件等
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。