所谓的标准事件流指的的:EMCAScript标准规定事件流包含三个阶段,分别为事件捕获阶段,处于目标阶段,事件冒泡阶段。
下面是一段html代码,根据代码来说明标准事件流。
<!DOCTYPE HTML> <html> <body> <div> <button>click</button> </div> </body> </html>
在上面的代码中,如果点击按钮button,则标准事件触发分别经历以下三个阶段:
事件触发一次经历三个阶段,所以我们在一个元素上注册事件也就可以在对应阶段注册事件,移除事件也同样。
target.addEventListener(type, listener, useCapture); //标准注册事件函数
//target:target: 文档节点、document、window 或 XMLHttpRequest。 //函数的参数,分别为 注册事件类型---type不包含on,事件的回调函数,事件注册在捕获期间还是冒泡期间
//例如:给button注册onclick事件,要是在捕获阶段注册,则 button.addEventListener("click",function(){},true);
target.removeEventListener(type, listener, useCapture); //在某一个元素上撤销已注册的事件。 这里强调的是 这里的函数必须与已注册的函数是同一个函数!
虽然大部分的浏览器都遵循着标准,但是在IE浏览器中,事件流却是非标准的。IE中事件流只有两个阶段: 处于目标阶段,冒泡阶段。
上面的HTML结构,如果是在IE中,事件流执行时如图所示:
对应着在IE中的事件注册和撤销事件函数:
target.attachEvent(type, listener); //target: 文档节点、document、window 或 XMLHttpRequest。
//函数参数: type----包含on.type一般为“onclick”,"onkeydown"
// listener:事件触发时的回调函数。
target.detachEvent(type,listener); //参数与注册参数相对应。
因为,IE中事件流没有捕获阶段,所以相应的在注册事件和撤销事件时比标准注册事件少一个参数。
为什么要单独提出事件的执行顺序? 是因为一些事件有其默认的行为,比如在文本框中按下鼠标左键,文本框文本框获得焦点;点击一个超链接,超链接进行跳转。 那么,事件的执行顺序是怎样的呢?
一般事件的执行顺序: 事件的捕获阶段====>处于目标阶段====>事件的冒泡阶段====>事件的默认行为。
正因为事件的默认行为是最后执行的,我们才得以机会阻止事件的默认行为。
如下,阻止文本框获取焦点:
//阻止文本框获取焦点
var input=document.getElementById("inputText"); input.onmousedown=function(event){ event=event||window.event; if(event.preventDefault){ //非IE浏览器阻止事件默认行为 event.preventDefault(); }else{ event.returnValue=false;//IE浏览器阻止事件默认行为 } }
因为IE中浏览器注册事件比较特殊,下面是一个跨浏览器注册函数。
var EventUtil = {
addEventListener: function (element, type, callback) { //注册事件,因为浏览器的兼容性考虑,注册事件一般都是注册在事件的冒泡阶段
if (element.addEventListener) {
element.addEventListener(type, callback, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, callback);
} else {
element['on' + type] = callback;
}
},
removeEventListener: function(element, type, callback) { //撤销事件
if (element.removeEventListener) {
element.removeEventListener(type, callback, false);
} else if (element.detachEvent) {
element.detachEvent('on' + type, callback);
} else {
element['on' + type] = null;
}
}
};
通过javaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序的属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,而且至今仍然为所有现在浏览器支持。原因主要有两点: 1.简单 2.具有跨浏览器优势。
每个元素(window和document)都有自己的事件处理程序属性,这些属性通常全部小写,例如 onclick, onmousedown.
var btn = document.getElementById('mybtn'); btn.onclick = function () { alert('click'); }
DOM0级事件有其自身的局限性,
1.那就是某一个属性只能赋值给一个函数,也就导致在某一个元素上的某一个事件属性只能对应着一个函数。多次注册时,已最 后一次注册为准。
2.DOM0级事件全部都是默认在冒泡阶段执行。
我们在第一部分所定义的跨浏览注册事件函数,就是一个DOM2级注册事件。DOM2级注册事件相比于DOM0级的优势就在于其可以多次注册,并且执行顺序与注册顺序一致。
var btn = document.getElementById('mybtn'); function fun1(){ alert("1");} function fun2(){ alert("2");}
EventUtil.addEventListener(btn,"click",fun1); //注册事件
EventUtil.addEventListener(btn,"click",fun1); //触发事件的时候会 先弹出 1 在弹出 2
如果不考虑IE浏览器,我们还可以在事件的捕获阶段,注册事件,但是一般因兼容性考虑,我们很少在事件的捕获阶段注册事件。
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有事件有关的信息。
例如,单击事件中会包含鼠标的位置信息,键盘触发的事件中会包含按下的键位有关的信息。
所有的浏览器都支持event,但支持的方式却有不同。
属性 | 类型 | 读写 | 描述 |
---|---|---|---|
bubbles | boolean | 只读 | 返回布尔值,指示事件是否是起泡冒泡 |
cancelable | boolean | 只读 | 返回布尔值,指示事件是否可拥可取消的默认动作。 |
currentTarget | Element | 只读 | 返回其事件监听器触发该事件的元素。 |
eventPhase | Intenger | 只读 | 返回事件传播的当前阶段。 |
target | Element | 只读 | 返回触发此事件的元素(事件的目标节点)。 |
timeStamp | Date | 只读 | 返回事件生成的日期和时间。 |
type | String | 只读 | 返回当前 Event 对象表示的事件的名称。 |
trusted | boolean | 只读 | 该事件是否是浏览器生成(true代表是,false代表是开发人员创建 |
preventDefault | Function | 只读 | 取消事件的默认行为在cancelable=true时有效 |
stopPropagation | Function | 只读 | 取消事件的捕获或者冒泡行为在bubbles=true时有效 |
在事件处理程序内部,对象this始终指向currentTarget的值,而target则只包含事件的实际目标。
属性 | 类型 | 读写 | 描述 |
---|---|---|---|
cancelBubble | boolean | 读/写 | 返回布尔值,指示事件是否是起泡冒泡 |
returnValue | boolean | 读/写 | 返回布尔值,指示事件是否可拥可取消的默认动作。 |
srcElement | Element | 只读 | 返回其事件监听器触发该事件的元素。 |
type | String | 只读 | 被触发事件的类型 |
上面的这些属性,是任何一个事件均会具有的属性。
在IE中有些srcElement对应着target;
执行event.returnValue=false对应着event.preventDefault();
执行event.cancelBubble=true对应着event.stopPropagation();
同时对于一些相关属性IE 比如 relatedTarget属性对应IE中的fromElement和toElement.属性.
因为IE和标准浏览器的不同,所以为了克服这样或者那样的问题,现在编写一个工具包克服兼容性的问题:
1 var EventUtil = { 2 addEventListener: function (element, type, callback) { //注册事件,因为浏览器的兼容性考虑,注册事件一般都是注册在事件的捕获阶段 3 if (element.addEventListener) { 4 element.addEventListener(type, callback, false); 5 } else if (element.attachEvent) { 6 element.attachEvent('on' + type, callback); 7 } else { 8 element['on' + type] = callback; 9 } 10 }, 11 getEvent:function(event){ //获取事件 12 return event||window.event; 13 }, 14 getTarget:function(event){ //获取事件的触发目标 15 return event.target||event.srcElement; 16 }, 17 preventDefault:function(event){ //阻止事件的默认行为 18 event.preventDefault?event.preventDefault():event.returnValue=false; 19 }, 20 stopPropagation:function(event){ //阻止事件冒泡 21 event.stopPropagation?event.stopPropagation:event.cancelBubble=true; 22 }, 23 removeEventListener: function(element, type, callback) { //撤销事件 24 if (element.removeEventListener) { 25 element.removeEventListener(type, callback, false); 26 } else if (element.detachEvent) { 27 element.detachEvent('on' + type, callback); 28 } else { 29 element['on' + type] = null; 30 } 31 } 32 };
每个事件在其被触发时,都有一些其特有的属性,比如键盘事件会有键位信息,鼠标事件会有会有位置信息。onmouseenter事件会有fromElement(IE)中,relatedTarget(非IE);onmouseover事件会有toElement(IE)中,relatedTarget(非IE).
详情在W3C教程中有进一步的叙述。W3C事件详解
非IE浏览器
创建鼠标事件的方法是createEvent()传入字符串“MouseEvent”.返回的对象有initMouseEvent()方法,这个方法有15个参数,分别与鼠标事件中某个典型的属性一一对应。
参数 | 类型 | 描述 |
type | String | 要触发的事件类型,例如‘click’。 |
bubbles | Boolean | 表示该事件是否能够被取消,针对鼠标事件模拟,该值应该被设置为true。 |
cancelable | Boolean | 表示该事件是被取消 |
view | AbstractView | 抽象视图:事件授予的视图,这个值几乎全是document.defaultView. |
detail | Intenger | 附加的事件信息这个初始化时一般应该默认为0。 |
screenX | Intenger | 事件距离屏幕左边的X坐标 |
screenY | Intenger | 事件距离屏幕上边的y坐标 |
clientX | Intenger | 事件距离可视区域左边的X坐标 |
clientY | Intenger | 事件距离可视区域上边的y坐标 |
ctrlKey | Boolean | 代表ctrol键是否被按下,默认为false。 |
altKey | Boolean | 代表alt键是否被按下,默认为false。 |
shiftKey | Boolean | Boolean类型 : 代表shif键是否被按下,默认为false |
metaKey | Boolean | 代表meta key 是否被按下,默认是false |
button | Intenger | 表示被按下的鼠标键,默认是零 |
relatedTarget | Elment | 事件的关联对象只有在模拟mouseover 和 mouseout时用到 |
使用方法如下:
var btn=document.getElementById("mybtn"); var event=document.createEvent("MouseEvent"); event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null); btn.dispatchEvent(event); //在这一步会设置event.target,以及触发事件类型
IE浏览器模拟鼠标事件
var event=document.createEventObject(); event.screenX=100; event.screenY=100; event.clientX=100; event.clientX=100; event.ctrlKey=false; btn.fireEvent("onclick",event); //在这一步会设置event.serElement,以及触发事件类型
键盘模拟事件是在DOM3规范中定义的。火狐浏览器根据草案定义了DOM2级中模拟键盘事件。在这里,我们讲述的是DOM3级规范,DOM3级不提倡使用oneypress事件。
DOM3标准
创建键盘事件的方法是createEvent()传入字符串“KeyboardEvent”.返回的对象有initKeyEvent()方法,这个方法有以下参数:
参数 | 类型 | 描述 |
type | String | 要触发的事件类型,例如"keydown". |
bubbles | Boolean | 表示该事件是否能够被取消,针对鼠标事件模拟,该值应该被设置为true。 |
cancelable | Boolean | 表示该事件是被取消 |
view | AbstractView | 被授予事件的是图. 通常值为:document.defaultView. |
key | string | 按下的键对应的code. |
location | integer | 按下键所在的位置. 0 :默认键盘, 1 左侧位置, 2 右侧位置, 3 数字键盘区, 4 虚拟键盘区, or 5 游戏手柄. |
modifiers | Boolean | 一个有空格分开的修饰符列表. |
repeat | integer | 一行中某个键被按下的次数 |
使用方式:
var textbox=document.getElementById("myTextBox"),event; if(document.implementation.hasFeature("KeyboardEvent",3.0)){ event=document.createEvent("KeyboardEvent"); event.initKeyboardEvent("keydown",true,true,document.defaultView,"a",0,"shift",0); textbox.dispatchEvent(event); }
并非所有的浏览器都实现了DOM3标准,下面看一下各个浏览器时怎么模拟鼠标事件。
FF浏览器
在FireFox中,调用createEvent()并传入KeyEvents就可以创建一个键盘事件。返回的事件对象会包含一个initKeyEvent()方法,这个方法接受一下10个参数。
参数 | 类型 | 描述 |
type | String | 要触发的事件类型,例如"keydown". |
bubbles | Boolean | 表示该事件是否能够被取消,针对鼠标事件模拟,该值应该被设置为true。 |
cancelable | Boolean | 表示该事件是被取消 |
view | AbstractView | 被授予事件的是图. 通常值为:document.defaultView. |
ctrlKey | Boolean | 表示是否按下了ctrl键位,默认值 false. |
altKey | Boolean | 表示是否按下了altl键位,默认值 false |
shiftKey | Boolean | 表示是否按下了shift键位,默认值 false. |
metaKey | Boolean | 表示是否按下了meta键位,默认值 false. |
KeyCode | Intenger | 被按下或者被释放的键位. 这个参数对keydown和keyup有用 |
charCode | Intenger | 通过按键生成的ASCII编码. 这个参数对keypress有用 |
//只适用于FF浏览器,在火狐浏览器中会在文本框中显示A
var textbox=document.getElementById("myTextBox"); //创建事件对象 var event=document.createEvent("keyEvents"); //初始化事件对象 event.initKeyEvent("keypress",true,true,document.defaultView,false,false,false,false,65,65); //触发事件 textbox.dispatchEvent(event);
非火狐非IE浏览器
在其他浏览器中,则需要创建一个通用事件,然后再向通用事件中添加键盘事件的特有信息。
//在其他浏览器中不能输入文本,这是因为非浏览器创建的事件并不能精确的模拟事件。
//创建事件对象 var event=document.createEvent("Events"); //初始化事件对象 event.initEvent(type,bubble,cancelable);
//初始化事件信息 event.view=document.defaultView; event.altKey=false; event.ctrlKey=false; ..... event.keyCode=65; event.charCode=65;
//触发事件
textbox.dispatchEvent(event);
IE浏览器
IE浏览器创建键盘事件和创建鼠标事件有点类似。如下所示:
var event=document.createEventObject(); event.altKey=false; event.ctrlKey=false; event.shiftKey=false; event.keyCode=65; textbox.fireEvent("onkeydown",event);
DOM3级还定义了"自定义事件"。自定义事件不同时DOM原生触发的,它的目的是让开发人员创建自己的事件。要创建新的自定义事件;
非IE浏览器
可以调用createEvent("CustomEvent")返回的对象有一个名为initCustomEvent()方法,接受如下四个参数:
参数 | 类型 | 描述 |
type | String | 要触发的事件类型,例如"keydown". |
bubbles | Boolean | 表示该事件是否能够被取消,针对鼠标事件模拟,该值应该被设置为true。 |
cancelable | Boolean | 表示该事件是被取消 |
detail | Boolean | 保存在event对象的detail属性中 |
//文档结构
<!DOCTYPE HTML> <html> <body> <div> <button id="button">click</button> <input type="text" id="inputText"/> </div> </body> </html>
//节本
var input=document.getElementById("inputText"); EventUtil.addEventListener(input,"myevent",function(event){ event=EventUtil.getEvent(event); alert(event.detail.message); //访问detail中的信息 });//注册时事件 var button=document.getElementById("button"); button.onclick=function(){ if(document.implementation.hasFeature("CustomEvents","3.0")){ var event=document.createEvent("CustomEvent"); event.initCustomEvent("myevent",true,false,{message:"helloworld"}); input.dispatchEvent(event); } //通过button按钮触发事件
}
IE浏览器自定义事件
//IE中document.createEventObject()方法不支持自定义的DOM事件....我们在有些前端框架中之所有能够实现自定义事件的各种浏览器兼容都是因为他们内部重写了一套事件机制来控制,才使得我们可以在各个浏览器上自定义事件。