一个小小的点击事件例子:
document.onclick=function(){
alert('a');
};
事件
事件的概念
事件:指的是文档或者浏览器窗口中发生的一些特定交互瞬间。我们可以通过侦听器(或者处理程序)来预定事件,以便事件发生的时候执行相应的代码。
事件对象
HTML DOM Event 对象
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
事件通常与函数结合使用,函数不会在事件发生前被执行!
事件对象的获取
IE中事件是作为全局对象window.event存在的,而在Firefox中则是作为句柄(handler)的第一个参数传入内的。
事件的获取
var evt=window.event||arguments[0];
第二种写法:function(ev){var e=ev||event;}
事件对象的应用
下面我们来讨论一下事件对象的实际运用。
事件处理程序
事件处理程序的概念
事件处理程序:我们用户在页面中进行的点击这个动作,鼠标移动的动作,网页页面加载完成的动作等,都可以称之为事件名称,即:click、mousemove、load等都是事件的名称。响应某个事件的函数则称为事件处理程序,或者叫做事件侦听器。
添加事件的两种方式
1.HTML事件处理程序
(1)直接在HTML元素行间属性里写JS代码
首先来举个栗子:
<div id="div1" onclick="alert('HTML事件')">DIV1</div>
这种直接在HTML元素行间属性里面写JS代码的方式,就是HTML事件处理的一种。
(2)定义一个函数,赋值给HTML元素的’on’+’xxx’属性
如下
<div id="div2" onclick="show()">DIV2</div>
<script type="text/javascript">
function show(){
alert('be clicked');
}
</script>
HTML事件处理程序比较常用,但还有以下几个小小的缺点:
一、HTML代码与JavaScript代码耦合紧密,没有实现相互分离(而W3C标准是提倡结构、表现与行为的分离),进行代码的更新与维护的时候就需要改动两处,增加了工作量。
二、如果像第一个例子里那么写,代码的通用性差,会导致整站的代码量大。而如果提取出来,存放在函数当中,那么,会面临另一个问题——当函数还没有被解析,只是HTML、CSS代码加载完毕,用户进行点击,会完全没有反应,甚至引发错误。
三、扩展事件处理程序的作用域链在不同浏览器当中会导致不同的结果。
因此,我们需要一个更好的方式来对事件进行处理。
2.JavaScript事件处理程序
在JS中指定事件处理程序有三种方式、下面我们一一进行讨论:
(1)DOM0级事件处理程序
举个栗子:
var oBtn=document.getElementById('btn1');//获取该按钮
oBtn.onclick=function(){//给按钮的点击事件绑定一个事件处理程序
alert(this.id+':I am clicked');
};
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理(当然,我们还没有说到事件流,这里先按下疑惑,下文中我们会讲到,暂时专心于事件的处理吧O(∩_∩)O)。
事件处理程序有添加,当然也该有删除,删除DOM0级事件处理程序的方法如下:
oBtn.onclick=null;//直接将该事件绑定的处理程序置空。
以该方式添加事件,IE6/7/8只支持window.event不支持参数传入,Firefox只支持参数传入不支持其它方式。IE9/Opera/Safari/Chrome 两种方式都支持。
这种方式虽然将JS于HTML完全分离了,但是也是一种比较早期的做法。而且也有一些不足之处:
一、每个事件只能绑定一个事件处理程序,如果绑定两次不同的事件处理程序,后绑定的会将前面绑定的处理程序置换。
二、只能在冒泡阶段被处理。
(2)DOM2级事件处理程序
DOM2级事件中定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。
所有DOM节点中都包含这两个方法,且它们都接受3个参数:要处理的事件名,做为事件处理程序的函数和一个布尔值。
最后这个参数如果是true,表示在捕获阶段调用事件处理程序;如果是fasle,表示在冒泡阶段调用事件处理程序。
同样举个栗子:
var oBtn2=document.getElementById('btn2');
oBtn2.addEventListener('click',function(){
alert('clicked');
},false);
这种方式的有点在于可以同时添加多个事件处理程序,并且它们会按顺序在第一到n次事件触发时按照添加顺序一一执行。
但是上面这种写法有一个十分大的问题,思考一下,是什么问题呢?
是的,移除。addEventListener添加的事件处理程序只能使用removeEventListener来移除,但是当在添加事件时直接添加一个匿名函数,会导致移除无法成功。
要移除事件句柄,addEventListener()的执行函数必须使用外部函数,所以进行一下优化:
var oBtn2=document.getElementById('btn2');
var show=function(){
alert('clicked);
}
oBtn2.addEventListener('click',show,false);
当然也可以这么写
var oBtn2=document.getElementById('btn2');
function show(){
alert('clicked);
}
oBtn2.addEventListener('click',show,false);
不知你是否注意到了,在DOM2级中,我们添加的事件名是没有on的,在运用的时候,一定要牢牢记住~以防出现什么低级错误^ ^
最后,大多数情况下,事件的处理程序都被添加到事件流的冒泡阶段,所以第三个参数一般是false
该方法符合W3C标准,大部分浏览器都支持,但不支持低版本IE,IE9已支持。
(3)IE事件处理程序
IE中有与DOM2级类似的两个方法:attachEvent()和detachEvent().
这两个方法也接收两个相同的参数:要处理的事件名和作为事件处理程序的函数,由于IE只支持冒泡,所以通过IE方式添加的事件处理程序只会被添加到冒泡阶段。
再来一个栗子:)
var oBtn3=document.getElementById('btn3');
oBtn3.addEvent('onclick',show);
同样的,attachEvent方法也能为元素添加多个事件处理程序,但是注意:事件触发时,这些函数的执行顺序与添加顺序完全相反。
当然,为了能够移除成功,这里的事件处理程序依然不推荐使用匿名函数。
该方法在IE和Opear和谷歌中有效,不支持FF
为了同时兼容IE/FF和其他主流浏览器,使得每次code的时候可更方便,我们来写一个自己的方法。
function addEventHandler(obj,e,fn){
if(obj.attachEvent){
obj.attachEvent('on'+e,fn);
}
else{
obj.addEventListener(e,fn,false);
}
}
来使用一下
addEventHandler(oBtn,'click',function(){alert('add success');});
事件流
概念
DOM(文档对象模型)结构是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素结点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为DOM事件流。
DOM2级事件流”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应。
两种事件模型
DOM同时支持两种事件模型:捕获型事件和冒泡型事件,但是,捕获型事件先发生。两种事件流会触发DOM中的所有对象,从document对象开始,也在document对象结束。
1.事件冒泡
IE的事件流叫做事件冒泡(event bubbling),事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)(目标节点)接收,然后逐级向上传播到较为不具体的节点(文档)——根节点(document)。
当事件在某一DOM元素被触发,向父节点、祖先节点冒泡时,每遇到依附有该事件类型处理器的节点都会触发事件处理程序。
在冒泡过程中的任何时候都可以终止事件的冒泡,在遵从W3C标准的浏览器里可以通过调用事件对象上的stopPropagation()方法,在IE中可以通过设置事件对象的cancelBubble属性为true。
如果不终止冒泡,事件将一直通过DOM冒泡直至到达文档根节点document.
2.事件捕获
事件捕获的思想是不太具体的节点应该更早的接收到事件,而最具体的节点应该在最后接收到节点。事件捕获的用意在于事件到达预定目标之前捕获它。
事件的处理将从DOM层次的根节点开始,从目标元素的所有祖先元素依次往下传递。在这个过程中,事件会被从文档根到事件目标元素之间各个继承派生的元素所捕获。
如果事件监听器在被注册时设置了useCapture属性为true,那么它们可以被分派给这期间的任何元素以对事件做出处理;否则,事件会被接着传递给派生元素路径上的下一元素,直至目标元素。
事件到达目标元素后,它会接着通过DOM节点再进行冒泡。
举个栗子
以简单的HTML页面为例,单击<div>
元素会按照下图顺序触发事件
在DOM事件流中,实际的目标<div>元素
在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从document到<html>
再到<body>
后就停止了。下一个阶段是“处于目标”阶段,于是事件在<div>
上发生,并在事件处理中被看成冒泡阶段的一部分。然后冒泡阶段发生,事件又传播回文档。
再总结一下:事件冒泡和事件捕获:事件冒泡是由目标节点一直往父节点、祖先节点直到被终止,否则会一直冒泡到文档对象模型的根节点document;而事件捕获则完全相反,事件捕获由根节点开始,从所有祖先元素一直往下传递到具体的目标节点。
兼容性:
Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。即:在这些浏览器中,有两次机会可以在目标对象上操作事件。
Opera、Firefox、Chrome和Safari都支持DOM事件流;IE不支持DOM事件流、只支持事件冒泡。
在DOM中,文本节点也触发事件、而IE中则不会
事件类型
事件类型有:
1.window事件
onload-完成加载
onunload-用户退出页面
onresize-窗口或框架被重新调整大小
onselect-文本被选中
onscroll-滚动带滚动条的元素中的内容
onabort-图像加载被中断
2.鼠标事件
onclick-对象被点击
ondbclick-双击
onmousedown-鼠标按键被按下
onmousemove-鼠标被移动
onmouseup-鼠标按键被松开
onmouseover-鼠标移到某元素之上
onmouseout-鼠标从某元素移开
oncontextmenu-鼠标点击右键
onmousewheel-鼠标滚轮
3.键盘事件
onkeydown-某键盘按键被按下
onkeyup-某键盘按键被松开
onkeypress-某键盘按键被按下并松开
4.表单事件
onsubmit-表单被提交
onreset-表单被重置
onchange-域中内容被修改
onfocus-元素获得焦点
onblur-元素失去焦点
oninput-输入
默认行为
什么是默认行为:
我的理解是:事件发生时浏览器默认的处理程序
阻止默认行为-阻止默认事件
普通写法return false;//既阻止了冒泡,也阻止了默认行为
-例1:阻止默认右键菜单
document.oncontextmenu=function(){
return false;
};
-例2.只能输入字母的输入框
var oTxt=document.getElementById('txt1');
oTxt.onkeydown=function(ev){
var e=ev||event;
if((e.keyCode>90||e.keyCode<65)&&e.keyCode!=8){
return false;
}
};
最后是一个实践-
磁性吸附
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>拖拽-磁性吸附</title>
<style type="text/css">
*{margin:0;padding: 0;}
#div1{
width: 100px;
height: 100px;
background: red;
position: absolute;
}
</style>
<script type="text/javascript">
window.onload=function(){
var oDiv=document.getElementById('div1');
oDiv.onmousedown=function(ev){
var e=ev||event;
var disX=e.clientX-oDiv.offsetLeft;
var disY=e.clientY-oDiv.offsetTop;
document.onmousemove=function(ev){
var e=ev||event;
var l=e.clientX-disX;
var t=e.clientY-disY;
if (l<50) {
l=0;
}
if (l>document.documentElement.clientWidth-oDiv.offsetWidth-50) {
l=document.documentElement.clientWidth-oDiv.offsetWidth;
}
if (t<50) {
t=0;
}
if (t>document.documentElement.clientHeight-oDiv.offsetHeight-50) {
t=document.documentElement.clientHeight-oDiv.offsetHeight;
}
oDiv.style.left=l+'px';
oDiv.style.top=t+'px';
};
document.onmouseup=function(){
document.onmousemove='';
document.onmouseup='';
};
return false;
};
}
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>