JS中的事件机制

1. ECMA的标准事件流

ECMAScript的标准事件流共分为三个阶段: 事件捕获阶段处于目标阶段事件冒泡阶段

以下是一个简单的例子:

1
2
3
4
5
6
7
8
<!DOCTYPE HTML>
<html>
<body>
<div>
<button>click</button>
</div>
</body>
</html>

点击按钮标准的事件触发分别经历以下三个阶段:

图片

事件触发一次经历三个阶段,所以我们在一个元素上注册事件也就可以在对应阶段注册事件,移除事件一样。例如:

1
2
3
4
5
6
7
8
//标准注册事件函数 target: 文档节点、document、window 或 XMLHttpRequest。type: 注册事件类型(不包含on),listener: 事件的回调函数,useCapture:事件注册在捕获期间还是冒泡期间
target.addEventListener(type, listener, useCapture);

//例如:给button注册onclick事件,要是在捕获阶段注册
button.addEventListener("click",function(){},true);

//在某一个元素上撤销已注册的事件。 这里强调的是 这里的listener必须与已注册的listener是同一个
target.removeEventListener(type, listener, useCapture);

2. IE中的事件流

IE中的事件是非标准的,总共分为两个阶段: 处于目标阶段冒泡阶段

上面提到的例子在IE中执行时这样的:

图片

IE中的事件注册与销毁事件

1
2
3
4
//IE注册事件函数 target: 文档节点、document、window 或 XMLHttpRequest。type: 注册事件类型(包含on) listener: 事件的回调函数 由于没有事件捕获阶段,所以没有useCapture参数
target.attachEvent(type, listener);

target.detachEvent(type,listener); //参数与注册参数相对应。

3. 事件的执行顺序

一般事件的执行顺序: 事件的捕获阶段==>处于目标阶段==>事件的冒泡阶段==>事件的默认行为

事件的默认行为是最后才执行的,所以我们可以阻止事件的默认行为。例如:

1
2
3
4
5
6
7
8
9
10
//阻止文本框获取焦点  
var input=document.getElementById("inputText");
input.onmousedown=function(event){
event=event||window.event;
if(event.preventDefault){ //非IE浏览器阻止事件默认行为
event.preventDefault();
}else{
event.returnValue=false; //IE浏览器阻止事件默认行为
}
}

4. 跨浏览器的注册事件

上面我们提到IE是非标准的事件流,所以要跨浏览器注册事件的话需要一些处理,下面是个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var EventUtil =  {
//注册事件,因为浏览器的兼容性考虑,注册事件一般都是注册在事件的冒泡阶段
addEventListener: function (element, type, callback) {
if (element.addEventListener) {
// 非IE
element.addEventListener(type, callback, false);
} else if (element.attachEvent) {
// IE
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;
}
}
};

5. DOM事件

  • DOM0级事件

    javaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序的属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,而且至今仍然为所有现在浏览器支持。原因主要有两点: 简单具有跨浏览器优势。 每个元素(window和document)都有自己的事件处理程序属性,这些属性通常全部小写,例如 onclick, onmousedown。例子:

    1
    2
    3
    4
    var btn = document.getElementById('mybtn');
    btn.onclick = function () {
    alert('click');
    }

    局限性:

    1.某一个属性只能赋值给一个函数,也就导致在某一个元素上的某一个事件属性只能对应着一个函数。多次注册时,已最后一次注册为准。

    2.DOM0级事件全部都是默认在冒泡阶段执行。

  • DOM2级事件

    我们在上面定义的跨浏览注册事件函数,就是一个DOM2级注册事件。DOM2级注册事件相比于DOM0级的优势就在于其可以多次注册,并且执行顺序与注册顺序一致。例子:

    1
    2
    3
    4
    5
    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

6. 事件对象

在触发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则只包含事件的实际目标。

  • IE中的事件对象
属性 类型 读写 描述
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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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;
    }
    },
    //获取事件
    getEvent:function(event){
    return event||window.event;
    },
    //获取事件的触发目标
    getTarget:function(event){
    return event.target||event.srcElement;
    },
    //阻止事件的默认行为
    preventDefault:function(event){
    event.preventDefault?event.preventDefault():event.returnValue=false;
    },
    //阻止事件冒泡
    stopPropagation:function(event){
    event.stopPropagation?event.stopPropagation:event.cancelBubble=true;
    },
    //撤销事件
    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;
    }
    }
    };

    每个事件在其被触发时,都有一些其特有的属性,比如键盘事件会有键位信息,鼠标事件会有会有位置信息。onmouseenter事件会有fromElement(IE)中,relatedTarget(非IE);onmouseover事件会有toElement(IE)中,relatedTarget(非IE).

7. 自定义事件

  • 模拟鼠标事件

    非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时用到
使用方法:
1
2
3
4
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浏览器

1
2
3
4
5
6
7
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 一行中某个键被按下的次数

使用方法:

1
2
3
4
5
6
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标准,下面看一下各个浏览器时怎么模拟鼠标事件。

FireFox浏览器

在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有用

使用方法:

1
2
3
4
5
6
7
8
//只适用于FireFox浏览器,在火狐浏览器中会在文本框中显示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浏览器 在其他浏览器中,则需要创建一个通用事件,然后再向通用事件中添加键盘事件的特有信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
//在其他浏览器中不能输入文本,这是因为非浏览器创建的事件并不能精确的模拟事件。
//创建事件对象
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浏览器创建键盘事件和创建鼠标事件有点类似。如下所示:

1
2
3
4
5
6
var event=document.createEventObject();
event.altKey=false;
event.ctrlKey=false;
event.shiftKey=false;
event.keyCode=65;
textbox.fireEvent("onkeydown",event);
  • 自定义DOM事件

    DOM3级还定义了”自定义事件”。自定义事件不同时DOM原生触发的,它的目的是让开发人员创建自己的事件。要创建新的自定义事件;

    非IE浏览器 可以调用createEvent(“CustomEvent”)返回的对象有一个名为initCustomEvent()方法,接受如下四个参数:

参数 类型 描述
type String 要触发的事件类型,例如”keydown”.
bubbles Boolean 表示该事件是否能够被取消,针对鼠标事件模拟,该值应该被设置为true。
cancelable Boolean 表示该事件是被取消
detail Boolean 保存在event对象的detail属性中

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE HTML>
<html>
<body>
<div>
<button id="button">click</button>
<input type="text" id="inputText"/>
</div>
<script>
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按钮触发事件
}
</script>
</body>
</html>

IE浏览器自定义事件

IE中document.createEventObject()方法不支持自定义的DOM事件。我们在有些前端框架中之所有能够实现自定义事件的各种浏览器兼容都是因为他们内部重写了一套事件机制来控制,才使得我们可以在各个浏览器上自定义事件。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • © 2019-2021 musi

请我喝杯咖啡吧~

支付宝
微信