JavaScript 事宜详解

JavaScript 事宜解读

1. 事宜基本概念

事宜是指在文档或许浏览器中发作的一些特定交互霎时,比方翻开某一个网页,浏览器加载完成后会触发 load 事宜,当鼠标悬浮于某一个元素上时会触发 hover 事宜,当鼠标点击某一个元素时会触发 click 事宜等等。

事宜处置惩罚就是当事宜被触发后,浏览器相应这个事宜的行动,而这个行动所对应的代码即为事宜处置惩罚递次

2. 事宜操纵:监听与移除监听

2.1 监听事宜

浏览器会依据一些事宜作出相对应的事宜处置惩罚,事宜处置惩罚的条件是须要监听事宜,监听事宜的要领重要有以下三种:

2.1.1 HTML 内联属性

即在 HTML 元素里直接填写与事宜相干的属性,属性值为事宜处置惩罚递次。示例以下:

<button onclick="console.log('You clicked me!');"></button>

onclick 对应着 click 事宜,所以当按钮被点击后,便会实行事宜处置惩罚递次,即控制台输出 You clicked me!

不过我们须要指出的是,这类体式格局将 HTML 代码与 JavaScript 代码耦合在一起,不利于代码的保护,所以应当只管防止运用如许的体式格局。

2.1.2 DOM 属性绑定

经由历程直接设置某个 DOM 节点的属性来指定事宜和事宜处置惩罚递次,上代码:

const btn = document.getElementById("btn");
btn.onclick = function(e) {
    console.log("You clicked me!");
};

上面示例中,起首取得 btn 这个对象,经由历程给这个对象增添 onclick 属性的体式格局来监听 click 事宜,这个属性值对应的就是事宜处置惩罚递次。这段递次也被称作 DOM 0 级事宜处置惩罚递次。

2.1.3 事宜监听函数

规范的事宜监听函数以下:

const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
    console.log("You clicked me!");
}, false);

上面的示例示意先取得示意节点的 btn 对象,然后在这个对象上面增添了一个事宜监听器,当监听到 click 事宜发作时,则调用回调函数,即在控制台输出 You clicked me!addEventListener 函数包括了三个参数 false,第三个参数的寄义在后面的事宜触发三个阶段以后再解说。这段递次也被称作 DOM 2 级事宜处置惩罚递次。IE9+、FireFox、Safari、Chrome 和 Opera 都是支撑 DOM 2 级事宜处置惩罚递次的,关于 IE8 及以下版本,则用 attacEvent() 函数绑定事宜。

所以我们能够写一段具有兼容性的代码:

function addEventHandler(obj, eventName, handler) {
    if (document.addEventListener) {
        obj.addEventListener(eventName, handler, false);
    }
    else if (document.attachEvent) {
        obj.attachEvent("on" + eventName, handler);
    }    
    else {
        obj["on" + eventName] = handler;
    }
}

2.2 移除事宜监听

在为某个元素绑定了一个事宜后,假如想打仗绑定,则须要用到 removeEventListener 要领。看以下例子:

const handler = function() {
    // handler logic
}
const btn = document.getElementById("btn");

btn.addEventListener("click", handler);
btn.removeEventListener("click", handler);

须要注重的是,绑定事宜的回调函数不能是匿名函数,必需是一个已被声明的函数,由于消除事宜绑定时须要通报这个回调函数的援用。

一样,IE8 及以下版本也不支撑上面的要领,而是用 detachEvent 替换。

const handler = function() {
    // handler logic
}
const btn = document.getElementById("btn");

btn.attachEvent("onclick", handler);
btn.detachEvent("onclick", handler);

一样,能够写一段具有兼容性的删除事宜函数:

function removeEventHandler(obj, eventName, handler) {
    if (document.removeEventListener) {
        obj.removeEventListener(eventName, handler, false);
    }
    else if (document.detachEvent) {
        obj,detachEvent("on" + eventName, handler);
    }
    else {
        obj["on" + eventName] = null;
    }
}

3. 事宜触发历程

事宜流形貌了页面吸收事宜的递次。当代浏览器(指 IE6-IE8 除外的浏览器,包括 IE9+、FireFox、Safari、Chrome 和 Opera 等)事宜流包括三个历程,离别是捕捉阶段、目的阶段和冒泡阶段,下图抽象地申明这个历程:

《JavaScript 事宜详解》

下面就细致地解说这三个历程。

3.1 捕捉阶段

当我们对 DOM 元素举行操纵时,比方鼠标点击、悬浮等,就会有一个事宜传输到这个 DOM 元素,这个事宜从 Window 最先,顺次经由 docuemnt、html、body,再不停经由子节点直到抵达目的元素,从 Window 抵达目的元素父节点的历程称为捕捉阶段,注重此时还未抵达目的节点。

3.2 目的阶段

捕捉阶段结束时,事宜抵达了目的节点的父节点,终究抵达目的节点,并在目的节点上触发了这个事宜,这就是目的阶段

须要注重的是,事宜触发的目的节点为最底层的节点。比方下面的例子:

<div>
    <p>你猜,目的在这里照样<span>那边</span>。</p>
</div>

当我们点击“那边”的时刻,目的节点是<span></span>,点击“这里”的时刻,目的节点是<p></p>,而当我们点击<p></p>地区以外,<div></div>地区以内时,目的节点就是<div></div>

3.3 冒泡阶段

当事宜抵达目的节点以后,就会沿着原路返回,这个历程有点相似水泡从水底浮出水面的历程,所以称这个历程为冒泡阶段

针对这个历程,wilsonpage 做了一个 DEMO,能够异常直观地检察这个历程。

如今再看 addEventListener(eventName, handler, useCapture) 函数。第三个参数是 useCapture,代表是不是在捕捉阶段举行事宜处置惩罚, 假如是 false, 则在冒泡阶段举行事宜处置惩罚,假如是 true,在捕捉阶段举行事宜处置惩罚,默许是 false。这么设想的重要原因是昔时微软和 netscape 之间的浏览器战役打得火热,netscape 主意捕捉体式格局,微软主意冒泡体式格局,W3C 采纳了折衷的体式格局,即先捕捉再冒泡。

4、事宜托付

上面我们讲了事宜的冒泡机制,我们能够运用这一特征来进步页面机能,事宜托付便事宜冒泡是最典范的运用之一。

何谓“托付”?在实际中,当我们不想做某件事时,便“托付”给别人,让别人代为完成。JavaScript 中,事宜的托付示意给元素的父级或许祖级,以至页面,由他们来绑定事宜,然后运用事宜冒泡的基本原理,经由历程事宜目的对象举行检测,然后实行相干操纵。看下面例子:

// HTML
<ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
</ul>

// JavaScript
var list = document.getElementById("list");
list.addEventListener("click", function(e) {
    console.log(e.target);
});

上面的例子中,5 个列表项的点击事宜均托付给了父元素 <ul id="list"></ul>

先看看事宜托付的可行性。有人会问,当事宜不是加在某个元素上的,怎样在这个元素上触发事宜呢?我们就是运用事宜冒泡的机制,事宜流抵达目的元素后会向上冒泡,此时父元素吸收到事宜流便会实行事宜实行递次。有人又会问,被托付的父元素下面假如有很多子元素,怎样晓得事宜流来自于哪个子元素呢?这个我们能够从事宜对象中的 target 属性取得。事宜对象下面会细致解说。

我们再来看看为何须要事宜托付。

  • 削减事宜绑定。上面的例子中,也能够离别给每一个列表项绑定事宜,但运用事宜托付的体式格局不仅省去了逐一绑定的贫苦,也提升了网页的机能,由于每绑定一个事宜便会增添内存运用。

  • 能够动态监听绑定。上面的例子中,我们对 5 个列表项举行了事宜监听,当删除一个列表项时不须要零丁删除这个列表项所绑定的事宜,而增添一个列表项时也不须要零丁为新增项绑定事宜。

看了上面的例子和诠释,我们能够看出事宜托付的中心就是监听一个 DOM 中更高层、更不详细的元素,比及事宜冒泡到这个不详细元素时,经由历程 event 对象的 target 属性来猎取触发事宜的详细元素。

5、阻挠事宜冒泡

事宜托付是事宜冒泡的一个运用,但有时刻我们并不愿望事宜冒泡。比方下面的例子:

const ele = document.getElementById("ele");
ele.addEventListener("click", function() {
    console.log("ele-click");
}, false);

document.addEventListener("click", function() {
    console.log("document-click");
}, false);

我们本意是当点击 ele 元素地区时显现 “ele-click”,点击其他地区时显现 “document-click”。然则我们发明点击 ele 元素地区时会顺次显现 “ele-click” “document-click”。那是由于绑定在 ele 上的事宜冒泡到了 document 上。想要处理这个题目,只须要加一行代码:

const ele = document.getElementById("ele");
ele.addEventListener("click", function(e) {
    console.log("ele-click");
    e.stopPropagation(); // 阻挠事宜冒泡
}, false);

document.addEventListener("click", function(e) {
    console.log("document-click");
}, false);

我们还能用 e.cancelBubble = true 来替换 e.stopPropagation()。网上的说法是 cancelBubble 仅仅适用于 IE,而 stopPropagation 适用于其他浏览器。但依据我试验的效果,当代浏览器(IE9 及 以上、Chrome、FF 等)均同时支撑这两种写法。为了保险起见,我们能够采纳以下代码:

function preventBubble(e) {
    if (!e) {
        const e = window.event;
    }
    e.cancelBubble = true;
    if (e.stopPropagation) {
        e.stopPropagation();
    }
}

6、event 对象

Event 对象代表事宜的状况,比方事宜在其中发作的元素、键盘按键的状况、鼠标的位置、鼠标按钮的状况。当一个事宜被触发的时刻,就会建立一个事宜对象。

我们用下面的代码打印出事宜对象:

<div id="list">
    <li>Item 1</li>
    <li>Item 2</li>
</div>
<script>
    var list = document.getElementById("list");
    list.addEventListener("click", function(e) {
        console.log(e);
    });
</script>

chrome 49 的运转效果以下:

《JavaScript 事宜详解》

下面引见一些比较经常使用的属性和要领。

target、 srcElement、 currentTarget 和 relatedTarget、fromElement、 toElement

  • target 与 srcElement 完全雷同;

  • target 指触发事宜的元素, currentTarget 指事宜所绑定的元素;

  • relatedTarget: 与事宜的目的节点相干的节点。关于 mouseover 事宜来讲,该属性是鼠标指针移到目的节点上时所脱离的谁人节点。关于 mouseout 事宜来讲,该属性是脱离目的时,鼠标指针进入的节点。
    关于其他范例的事宜来讲,这个属性没有用;

  • fromElement 和 toElement 仅仅关于 mouseover 和 mouseout 事宜有用。

以上面的例子申明,当点击 <li>Item 1</li> 时,target 就是 <li>Item 1</li> 元素,而 currentTarget 是 <div id="list"></div>

clientX/Y、 screenX/Y、 pageX/Y、 offsetX/Y

上图:

《JavaScript 事宜详解》

  • offsetX/Y: 点击位置相关于所处元素左上角的位置;

  • clientX/Y: 点击位置相关于浏览器内容地区左上角的位置;

  • screenX/Y: 点击位置相关于屏幕左上角的位置;

  • pageX/Y: 点击位置相对整张页面左上角的位置;

  • pageX/Y 与 clientX/Y 平常情况下会雷同,只要涌现滚动条时才不一样。

altKey、 ctrlKey、 shiftKey

  • altKey: 返回当事宜被触发时,”ALT” 是不是被按下;

  • ctrlKey: 返回当事宜被触发时,”CTRL” 键是不是被按下;

  • shiftKey: 返回当事宜被触发时,”SHIFT” 键是不是被按下;

其他属性

  • type: 返回当前 Event 对象示意的事宜的称号

  • bubbles: 返回布尔值,指导事宜是不是是起泡事宜范例;

  • cancelable: 返回布尔值,指导事宜是不是可拥可取消的默许行动;

  • eventPhase: 返回事宜流传的当前阶段,有三个值: Event.CAPTURING_PHASE、 Event.AT_TARGET、 Event.BUBBLING_PHASE,对应的值为 1、2、3,离别示意捕捉阶段、一般事宜派发和起泡阶段;

  • path: 冒泡阶段经由的节点;

要领

  • preventDefault(): 关照浏览器不要实行与事宜关联的默许行动;

  • stopPropagation(): 阻挠冒泡;

参考及拓展浏览:

  1. HTML DOM Event 对象

  2. 最细致的JavaScript和事宜解读

  3. JavaScript Events

  4. [解惑]JavaScript事宜机制

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