【重温基本】20.事宜

本文是 重温基础 系列文章的第二十篇。

这是第三个基础系列的第一篇,迎接延续关注呀!
重温基础 系列的【低级】和【中级】的文章,已一致整顿到我的【Cute-JavaScript】的JavaScript基础系列中。

本日感觉:影戏有时候看的是缘分。

系列目次:

本章节温习的是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事宜就会根据下面递次流传:

  1. <div>
  2. <body>
  3. <html>
  4. document

因而可知,元素绑定的事宜会经由历程DOM树向上流传,每层节点都邑发作,直到document对象,如图展现了冒泡历程:
《【重温基本】20.事宜》

1.2 事宜捕捉

事宜捕捉(Event Capturing):让不太细致的节点更早吸收事宜,而最细致的节点末了吸收事宜,即在事宜抵达预定目的之前捕捉到,看下示例代码(HTML代码和前面一样),事宜捕捉的历程是如许的:

  1. document
  2. <html>
  3. <body>
  4. <div>

看得出,document对象最新吸收事宜,然后沿DOM树顺次向下,直到末了的现实目的<div>元素,如图展现了捕捉历程:

《【重温基本】20.事宜》

注重:由于老版本的浏览器不支撑,因而很少人运用事宜捕捉,不过假如特别需求还是可以运用事宜捕捉,发起还是运用事宜冒泡。

1.3 DOM事宜流

“DOM2级事宜”划定的事宜流包含三个阶段:事宜捕捉阶段处于目的阶段事宜冒泡阶段
事宜捕捉为截获事宜供应时机,然后现实的目的吸收到事宜,末了事宜冒泡,对事宜作出响应。根据前面的HTML代码,悉数流程是如许的:

《【重温基本】20.事宜》

在DOM事宜流中,现实目的(<div>元素)在捕捉阶段不吸收事宜,即在捕捉阶段,事宜从document对象<html>再到<body>后就住手,进入“处于目的”阶段,事宜在<div>元素上发作,然后才进入冒泡阶段,将事宜传回给文档。

注重:现在主流浏览器都支撑DOM事宜流,只要IE8和之前版本不支撑。

2.事宜处置惩罚

事宜处置惩罚,即响应某个事宜。我们把事宜处置惩罚的函数,称为“事宜处置惩罚顺序”。
事宜处置惩罚顺序的称号平常都以on开首,如click事宜的事宜处置惩罚顺序就是onclickload事宜的事宜处置惩罚顺序就是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个瑕玷:

  1. 存在时间差

能够涌现如许的状况:HTML元素触发事宜,然则事宜处置惩罚顺序还未定义(函数的定义在HTML最底下定义),就会涌现报错,这与HTML代码加载递次有关。

  1. 作用域链的非常

由于差别浏览器JavaScript引擎遵照的标识符剖析划定规矩存在差别,致使接见非限制对象成员时失足,表现为事宜处置惩罚顺序的作用域链在差别浏览器结果差别。

  1. HTML和JavaScript代码严密耦合

这常常就是许多开发人员摒弃HTML事宜处置惩罚顺序的缘由。

2.2 DOM0级事宜处置惩罚顺序

经由历程赋值情势,将一个函数赋值给一个事宜处置惩罚顺序属性。每一个元素(包含windowdocument)都有本身的事宜处置惩罚属性,这些属性平常悉数小写,如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 跨浏览器事宜处置惩罚顺序

在做跨浏览器事宜处置惩罚顺序,我们可以有两种体式格局:

  1. 运用可以断绝浏览器差别的JavaScript库
  2. 零丁手写一个处置惩罚要领

我们零丁受写一个处置惩罚要领也不难,只需关注好事宜冒泡阶段,我们可以建立一个要领,辨别运用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对象包含与建立它的特定事宜相干的属性和要领,常常有以下成员:
《【重温基本】20.事宜》

《【重温基本】20.事宜》

我们可以运用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,虽然mouseovermouseout事宜也有冒泡,但由于不好处置惩罚它们而且常常须要从新盘算元素位置。

可以看出,事宜托付有以下长处:

  • 削减内存斲丧
  • 动态绑定事宜

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 : 对象,示意与事宜相干的对象,只在mouseovermouseout时运用

案例:

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()要领会自动增加srcElementtype属性,我们须要手动增加其他属性,下面模仿一个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);

参考文章

  1. 《JavaScript高等顺序设计》第13章 事宜

本部分内容到这完毕

Author王安然
E-mailpingan8787@qq.com
博 客www.pingan8787.com
微 信pingan8787
逐日文章引荐https://github.com/pingan8787…
JS小册js.pingan8787.com

迎接关注微信民众号【前端自习课】天天清晨,与您一同进修一篇优异的前端手艺博文 .

    原文作者:pingan8787
    原文地址: https://segmentfault.com/a/1190000018035007
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞