本文是 重温基础 系列文章的第二十篇。
这是第三个基础系列的第一篇,迎接延续关注呀!
重温基础 系列的【低级】和【中级】的文章,已一致整顿到我的【Cute-JavaScript】的JavaScript基础系列中。
本日感觉:影戏有时候看的是缘分。
系列目次:
- 【温习材料】ES6/ES7/ES8/ES9材料整顿(个人整顿)
- 【重温基础】1-14篇
- 【重温基础】15.JS对象引见
- 【重温基础】16.JSON对象引见
- 【重温基础】17.WebAPI引见
- 【重温基础】18.相称性推断
- 【重温基础】19.闭包
本章节温习的是JS中的事宜,事宜冒泡捕捉代办模仿等等。
前置学问:
JavaScript与HTML的交互式经由历程事宜来完成的,是文档或浏览器窗口中发作的一些特定的交互霎时。
1.事宜流
事宜流形貌的是从页面中吸收事宜的递次,平常有如许两种完整相反的事宜流观点:事宜冒泡流(IE团队提出)和事宜捕捉流(网景团队提出)。
1.1 事宜冒泡
冒泡事宜(Event Bubbling):事宜最先时由最细致的元素吸收(文档中嵌套条理最深的谁人节点),然后逐层向上流传到较为不细致的节点(文档),看下示例代码:
<!DOCTYPE html>
<html>
<head>
<title>leo 事宜冒泡</title>
</head>
<body>
<div id="leo">点击</div>
</body>
</html>
点击页面中<div>
元素,这个click
事宜就会根据下面递次流传:
<div>
<body>
<html>
document
因而可知,元素绑定的事宜会经由历程DOM树向上流传,每层节点都邑发作,直到document对象
,如图展现了冒泡历程:
1.2 事宜捕捉
事宜捕捉(Event Capturing):让不太细致的节点更早吸收事宜,而最细致的节点末了吸收事宜,即在事宜抵达预定目的之前捕捉到,看下示例代码(HTML代码和前面一样),事宜捕捉的历程是如许的:
document
<html>
<body>
<div>
看得出,document对象
最新吸收事宜,然后沿DOM树顺次向下,直到末了的现实目的<div>
元素,如图展现了捕捉历程:
注重:由于老版本的浏览器不支撑,因而很少人运用事宜捕捉,不过假如特别需求还是可以运用事宜捕捉,发起还是运用事宜冒泡。
1.3 DOM事宜流
“DOM2级事宜”划定的事宜流包含三个阶段:事宜捕捉阶段,处于目的阶段和事宜冒泡阶段。
事宜捕捉为截获事宜供应时机,然后现实的目的吸收到事宜,末了事宜冒泡,对事宜作出响应。根据前面的HTML代码,悉数流程是如许的:
在DOM事宜流中,现实目的(<div>
元素)在捕捉阶段不吸收事宜,即在捕捉阶段,事宜从document对象
到<html>
再到<body>
后就住手,进入“处于目的”阶段,事宜在<div>
元素上发作,然后才进入冒泡阶段,将事宜传回给文档。
注重:现在主流浏览器都支撑DOM事宜流,只要IE8和之前版本不支撑。
2.事宜处置惩罚
事宜处置惩罚,即响应某个事宜。我们把事宜处置惩罚的函数,称为“事宜处置惩罚顺序”。
事宜处置惩罚顺序的称号平常都以on
开首,如click
事宜的事宜处置惩罚顺序就是onclick
,load
事宜的事宜处置惩罚顺序就是onload
。
我们将事宜处置惩罚顺序,分为这么几类:
- HTML事宜处置惩罚顺序
- DOM0级事宜处置惩罚顺序
- DOM2级事宜处置惩罚顺序
- IE事宜处置惩罚顺序
- 跨浏览器事宜处置惩罚顺序
2.1 HTML事宜处置惩罚顺序
某个元素支撑的事宜,都可以用一个与响应事宜处置惩罚顺序同名的HTML特征来指定,这个特征的值应该是可以实行的JavaScript代码。比方:
<input type="button" value="点击" onclick="alert('hello leo');">
也可以把须要实行的细致事宜零丁定义出来,可以安排与零丁.js
文件,也可以在文档内用<script>
标签引入:
function fun(){
alert('hello leo');
}
<input type="button" value="点击" onclick="fun()">
我们经由历程如许指定事宜处置惩罚顺序,可以有一个局部变量event
来猎取事宜对象自身,在这个函数内部,this
值即是这个变量event
。
<input type="button" value="点击" onclick="fun(event)">
别的,HTML中指定事宜处置惩罚顺序,会有2个瑕玷:
- 存在时间差
能够涌现如许的状况:HTML元素触发事宜,然则事宜处置惩罚顺序还未定义(函数的定义在HTML最底下定义),就会涌现报错,这与HTML代码加载递次有关。
- 作用域链的非常
由于差别浏览器JavaScript引擎遵照的标识符剖析划定规矩存在差别,致使接见非限制对象成员时失足,表现为事宜处置惩罚顺序的作用域链在差别浏览器结果差别。
- HTML和JavaScript代码严密耦合
这常常就是许多开发人员摒弃HTML事宜处置惩罚顺序的缘由。
2.2 DOM0级事宜处置惩罚顺序
经由历程赋值情势,将一个函数赋值给一个事宜处置惩罚顺序属性。每一个元素(包含window
和document
)都有本身的事宜处置惩罚属性,这些属性平常悉数小写,如onclick
,将这类属性的值设置成一个函数,就可以指定事宜处置惩罚顺序:
var leo = document.getElementById('leo');
leo.onclick = function(){
alert('hello leo!');
}
运用DOM0级要领指定事宜处置惩罚顺序,被认为是元素的要领。此时的事宜处置惩罚顺序是在元素的作用域实行,那末,this就援用当前元素,可以接见元素的任何属性和要领:
var leo = document.getElementById('leo');
leo.onclick = function(){
alert(this.id); // "leo"
}
我们也可以经由历程设置事宜处置惩罚顺序属性来删除DOM0级的事宜处置惩罚顺序。
leo.onclick = null;
2.3 DOM2级事宜处置惩罚顺序
有2个要领:
- 增加事宜处置惩罚顺序:
addEventListener()
- 删除事宜处置惩罚顺序:
removeEventListener()
一切的DOM节点都包含这两个要领,而且它们都吸收三个参数:
- 处置惩罚的事宜称号
- 事宜处置惩罚顺序的函数
- 布尔值(true:事宜捕捉阶段挪用,false:事宜冒泡阶段挪用)
var leo = document.getElementById('leo');
leo.addEventListener('click',function(){
alert(this.id); // "leo"
},false);
与DOM0级要领一样,这里的事宜处置惩罚顺序也会是在元素的作用域中实行,因而this也是指向元素,可以接见元素的任何属性和要领。
运用DOM2级要领,可以增加多个事宜处置惩罚顺序,并根据增加递次触发:
var leo = document.getElementById('leo');
leo.addEventListener('click',function(){
alert(this.id); // "leo"
},false);
leo.addEventListener('click',function(){
alert('hello leo!'); // "hello leo!"
},false);
注重:经由历程addEventListener()
增加的事宜只能经由历程removeEventListener()
移除,而且二者传入的参数一致,这就意味着经由历程addEventListener()
增加的匿名函数不能被移除,看下面代码:
var leo = document.getElementById('leo');
leo.addEventListener('click',function(){
alert(this.id); // "leo"
},false);
// 没有结果
leo.removeEventListener('click',function(){
// do some thing
},false);
没有结果是由于这两个要领传入的函数,是完整差别的,为了到达删除事宜处置惩罚顺序的结果,我们可以将处置惩罚函数零丁定义出来:
var leo = document.getElementById('leo');
var fun = function(){
alert(this.id);
}
leo.addEventListener('click',fun,false);
// 有结果
leo.removeEventListener('click',fun,false);
2.4 IE事宜处置惩罚顺序
IE完成两种要领: attachEvent()
和detachEvent()
。这两个要领吸收雷同的两个参数:事宜处置惩罚顺序称号和事宜处置惩罚顺序函数。
由于IE8和更早版本只支撑事宜冒泡,所以经由历程attachEvent()
增加的事宜处置惩罚顺序会被增加到冒泡阶段。
var leo = document.getElementById('leo');
leo.attachEvent('onclick',function(){
alert('hello leo'); // "leo"
},false);
// attachEvent也支撑增加多个事宜处置惩罚顺序
leo.attachEvent('onclick',function(){
alert('hello world'); // "leo"
},false);
注重:这里的第一个参数是onclick
而不是DOM的addEventListener()
的click
。
IE的attachEvent()和DOM0级要领辨别:
二者事宜处置惩罚顺序的作用域差别。
- DOM0级要领,作用域在所属元素的作用域。
- attachEvent(),作用域在全局作用域,即
this
指向window
。
和DOM0级要领一样,detachEvent()
只能移除运用attachEvent()
增加的要领,为了防止没法移除,也是须要将处置惩罚的函数零丁定义出来:
var leo = document.getElementById('leo');
var fun = function(){
alert(this.id);
}
leo.attachEvent('onclick',fun,false);
// 有结果
leo.detachEvent('onclick',fun,false);
2.6 跨浏览器事宜处置惩罚顺序
在做跨浏览器事宜处置惩罚顺序,我们可以有两种体式格局:
- 运用可以断绝浏览器差别的JavaScript库
- 零丁手写一个处置惩罚要领
我们零丁受写一个处置惩罚要领也不难,只需关注好事宜冒泡阶段,我们可以建立一个要领,辨别运用DOM0级要领、DOM2级要领或IE要领即可,默许采纳DOM0级要领。
var my_event = {
addMyEvent: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;
}
};
removeMyEvent: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;
}
}
}
3.事宜对象
当触发一个DOM上的事宜时,都邑发生一个事宜对象event
,并作为参数传入事宜处置惩罚顺序,这个对象包含一切与事宜相干的信息。包含致使事宜的元素、事宜范例等其他信息。
3.1 DOM中的事宜对象
不管指定事宜处置惩罚顺序时运用什么要领(DOM0级要领或DOM2级要领),都邑传入event
对象:
var leo = document.getElementById('leo');
leo.onclick = function(event){
alert(event.type); // 'click'
}
leo.addEventListener('click',function(event){
alert(event.type); // 'click'
},false);
event
对象包含与建立它的特定事宜相干的属性和要领,常常有以下成员:
我们可以运用event
中的对象和要领:
- 阻挠事宜的默许行动
var leo = document.getElementById('leo');
leo.onclick = function(event){
// 只要当 event 中的 cancelable 属性为true的事宜
event.preventDefault();
}
- 马上住手事宜在DOM的流传
经由历程挪用event.stopPropagation()
要领防止弹框涌现两次。
var leo = document.getElementById('leo');
leo.onclick = function(event){
alert('leo');
event.stopPropagation();
}
document.body.onclick = function(event){
alert('hello leo');
}
3.2 IE中的事宜对象
接见IE中的事宜对象event
,要领有多种,取决于事宜处置惩罚顺序的要领:
- DOM0级要领,运用
window.event
var leo = document.getElementById('leo');
leo.onclick = function(){
var event = window.event;
alert(event.type); // 'click'
}
- IE的
attachEvent
要领,event
作为参数传入(也可以运用window.event
)
var leo = document.getElementById('leo');
leo.attachEvent('onclick',function(event){
alert(event.type); // 'click'
},false);
3.3 跨浏览器的事宜对象
虽然DOM和IE中event
对象差别,但我们也可以和前面的 跨浏览器事宜处置惩罚顺序 处置惩罚一样,经由历程他们之间的辨别,完成映照:
var my_event = {
myAddFun : function(element, type, handler){
// do some thing
},
// 返回对event的援用
getMyEvent : function(event){
return event?event:window.event;
},
// 返回事宜的目的
getMyTarget : function(event){
return event.target || event.srcElement;
},
// 作废事宜的默许行动
preventDefault : function(event){
if(event.preventDefault){
event.preventDefault();
}else {
event.returnValue = false;
}
},
myRemoveFun : function(element, type, handler){
// do some thing
},
// 阻挠事宜流
stopPropagetion : function(event){
if(event.stopPropagetion){
event.stopPropagetion();
}else {
event.cancelBubble = true;
}
}
}
var leo = document.getElementById('leo');
leo.onclick = function(event){
alert('leo');
event = my_event(event);
my_event.stopPropagation(event);
}
leo.onclick = function(event){
alert('hello world');
}
4.事宜范例
Web浏览器中的事宜范例有许多,DOM3级事宜划定有以下几类事宜范例:
- UI事宜:当用户与页面上元素交互时触发;
- 核心事宜:当元素落空或猎取核心时触发;
- 鼠标事宜:当用户经由历程鼠标在页面操纵时触发;
- 滚轮事宜:当运用鼠标滚轮(或类似装备)时触发;
- 文本事宜:当在文档中输入文本时触发;
- 键盘事宜:当用户经由历程键盘操纵时触发;
- 合成事宜:当为IME输入字符时触发;
- 更改事宜:当底层DOM构造更改时触发;
细致每一个要领的细致引见,可以检察W3school HTML DOM Event 对象
或许检察《JavaScript高等顺序设计(第三版)》的第362页最先。
我后面会零丁整顿一篇,引见这些事宜的文章。
5.事宜托付
简朴明白就是讲一个响应事宜托付到另一个元素。
事宜托付应用事宜冒泡,指定一个事宜处置惩罚顺序来治理某一范例的一切事宜,比方我们经由历程一个函数来替代其他三个函数:
<ul id="btn">
<li id="btn1">按钮1</li>
<li id="btn2">按钮2</li>
<li id="btn3">按钮3</li>
</ul>
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var btn3 = document.getElementById('btn3');
// my_event 在前面定义了
my_event.myAddFun(btn1, 'click', function(event){
alert('btn1点击');
});
my_event.myAddFun(btn2, 'click', function(event){
alert('btn2点击');
});
my_event.myAddFun(btn3, 'click', function(event){
alert('btn3点击');
});
下面我们在DOM树层级更高的元素上增加一个事宜处置惩罚函数,来做事宜托付,我们这么重写这段代码:
var btn = document.getElementById('btn');
my_event.myAddFun(btn, 'click', function(event){
event = my_event.getMyEvent(event);
var target = my_event.getMyTarget(event);
switch(target.id){
case "btn1":
alert('btn1点击');
break;
case "btn2":
alert('btn2点击');
break;
case "btn3":
alert('btn3点击');
break;
}
});
最适合采纳事宜托付手艺的事宜包含:click
/mousedown
/mouseup
/keyup
/keydown
/keypress
,虽然mouseover
和mouseout
事宜也有冒泡,但由于不好处置惩罚它们而且常常须要从新盘算元素位置。
可以看出,事宜托付有以下长处:
- 削减内存斲丧
- 动态绑定事宜
6.事宜模仿
JavaScript的事宜模仿重要用来在恣意时候触发特定事宜。
6.1 DOM中的事宜模仿
在document
对象上运用createEvent()
要领建立一个event
对象。 createEvent()
吸收一个参数,即要建立的事宜范例的字符串。
DOM2级中,一切这些字符串都运用英文复数情势,DOM3级中都变成单数,也可所以下面中的字符串:
- UIEvents : 平常化的UI事宜(鼠标和键盘事宜都继续自UI事宜)(DOM3级中
UIEvent
) - MouseEvents : 平常化的鼠标事宜(DOM3级中
MouseEvent
) - MutationEvents : 平常化的DOM转动事宜(DOM3级中
MutationEvent
) - HTMLEvents : 平常化的HTML事宜(DOM3级中
HTMLEvent
)
建立event
以后,我们须要运用dispatchEvent()
要领去触发这个事宜,须要传入一个参数,参数是须要触发事宜的event对象。
一切支撑事宜的DOM节点都支撑这个要领。事宜触发以后,事宜就可以还是冒泡并激发响应事宜处置惩罚顺序的实行。
6.1.1 模仿鼠标事宜
运用createEvent()
要领传入MouseEvents
建立一个鼠标事宜,返回的对象有一个initMouseEvent()
要领,用于指定与该鼠标事宜相干的信息,有15个参数:
- type : 字符串,示意触发的事宜范例,如
click
- bubble : 布尔值,示意是不是冒泡,为了准确模仿鼠标事宜,平常设置为true
- cancelable :布尔值,示意是不是可以作废,为了准确模仿鼠标事宜,平常设置为true
- view : 与事宜关联的视图,基础都设置为
document.defaultView
- detail : 整数,与事宜有关的细致信息,基础设置为0
- screenX : 整数,事宜相对屏幕的X坐标
- screenY : 整数,事宜相对屏幕的Y坐标
- clientX : 整数,事宜相对视口的X坐标
- clientY : 整数,事宜相对视口的Y坐标
- ctrlKey : 布尔值,示意是不是按下Ctrl键,默许false
- altKey : 布尔值,示意是不是按下Alt键,默许false
- shiftKey : 布尔值,示意是不是按下Shift键,默许false
- metaKey : 布尔值,示意是不是按下Meta键,默许false
- button : 整数,示意按下哪一个鼠标键,默许0
- relatedTarget : 对象,示意与事宜相干的对象,只在
mouseover
和mouseout
时运用
案例:
var btn = document.getElementById('btn');
var myEvent = document.createEvent('MouseEvents');
myEvent.initMouseEvent(
'click', true, true, document.defaultView,
0, 0, 0, 0, 0,
false, false, false, false, 0, null
)
btn.dispatchEvent(myEvent);
6.1.2 模仿键盘事宜
DOM3级划定,运用createEvent()
要领传入KeyboardEvent
建立一个键盘事宜,返回的对象有一个initKeyEvent()
要领,有8个参数:
- type : 字符串,示意触发的事宜范例,如
keydown
- bubble : 布尔值,示意是不是冒泡,为了准确模仿键盘事宜,平常设置为true
- cancelable :布尔值,示意是不是可以作废,为了准确模仿键盘事宜,平常设置为true
- view : 与事宜关联的视图,基础都设置为
document.defaultView
- key : 整数,示意按下的键的键码
- localtion : 整数,示意按下那里的键,默许0示意主键盘,1示意左,2示意右,3示意数字键盘,4示意挪动装备(即假造键盘),5示意手柄
- modifiers : 字符串,空格分开的修正件列表,如”shift”
- repeat : 整数,在一行中按了多少次这个键
由于DOM3级不首倡运用keypress
事宜,因而只能用这个体式格局来模仿keyup
/keydown
事宜。
模仿按住Shift和A键的案例:
var btn = document.getElementById('btn');
var myEvent;
// 以DOM3级体式格局建立
if(document.implementation.hasFeature('KeyboardEvents', '3.0')){
myEvent = document.createEvent('KeyboardEvent');
myEvent.initKeyboardEvent(
'keydown', true, true, document.defaultView,
'a', 0, 'Shift', 0
);
}
btn.dispatchEvent(myEvent);
6.1.3 模仿其他事宜
如模仿更改事宜和HTML事宜。
- 模仿更改事宜
经由历程createEvent()
传入MutationEvents
参数建立,返回一个initMutationEvent()
要领,这个要领吸收参数包含:type
/bubbles
/cancelable
/relatedNode
/preValue
/newValue
/attrName
/attrChange
,下面模仿一个案例:
var btn = document.getElementById('btn');
var myEvent = document.createEvent('MutationEvents');
myEvent.initMutationEvent(
'DOMNodeInserted', true, false, someNode, '', '', '', 0
);
btn.dispatchEvent(myEvent);
- 模仿HTML事宜
经由历程createEvent()
传入HTMLEvents
参数建立,返回一个initEvent()
要领,下面模仿一个案例:
var btn = document.getElementById('btn');
var myEvent = document.createEvent('HTMLEvents');
myEvent.initEvent('focus', true, false);
btn.dispatchEvent(myEvent);
6.1.4 自定义DOM事宜
经由历程createEvent()
传入CustomEvent
参数建立,返回一个initCustomEvent()
要领,有4个参数:
- type : 字符串,示意触发的事宜范例,如
keydown
- bubble : 布尔值,示意是不是冒泡,为了准确模仿键盘事宜,平常设置为true
- cancelable :布尔值,示意是不是可以作废,为了准确模仿键盘事宜,平常设置为true
- detail : 对象,恣意值,保存在
event
对象的detail
属性中
案例:
var btn = document.getElementById('btn');
var myEvent;
// my_event在前面定义 2.6 跨浏览器事宜处置惩罚顺序
my_event.addMyEvent(btn, 'myevent', function(event){
alert('btn detail ', event.detail);
});
my_event.addMyEvent(document, 'myevent', function(event){
alert('document detail ', event.detail);
});
// 以DOM3级体式格局穿件
if(document.implementation.hasFeature('CustomEvents', '3.0')){
myEvent = document.createEvent('CustomEvent');
myEvent.initCustomEvent(
'myevent', true, false, 'hello leo',
);
btn.dispatchEvent(myEvent);
}
6.2 IE中的事宜模仿
IE8及之前的版本模仿事宜和DOM中模仿思绪类似:想建立event对象
再指定信息,末了触发。
辨别在于,IE中运用document.createEventObject()
要领建立event对象
,而且不吸收参数,返回一个通用event对象
,我们要做的就是给这个event对象
增加信息,末了在目的上挪用fireEvent()
要领,并接收两个参数(事宜处置惩罚顺序的称号和event对象
)。
在挪用fireEvent()
要领会自动增加srcElement
和type
属性,我们须要手动增加其他属性,下面模仿一个click事宜:
var btn = document.getElementById('btn');
var myEvent = document.createEventObject();
myEvent.screenX = 100;
myEvent.screenY = 0;
myEvent.clientX = 100;
myEvent.clientY = 0;
myEvent.ctrlKey = false;
myEvent.altKey = false;
myEvent.shiftKey = false;
myEvent.button = 0;
btn.fireEvent('onclick', event);
参考文章
- 《JavaScript高等顺序设计》第13章 事宜
本部分内容到这完毕
Author | 王安然 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
逐日文章引荐 | https://github.com/pingan8787… |
JS小册 | js.pingan8787.com |
迎接关注微信民众号【前端自习课】天天清晨,与您一同进修一篇优异的前端手艺博文 .