乐趣区

关于javascript:前端-面试-JavaScript-之你不一定会的基础题二

最近我在做前端面试题总结系列,感兴趣的敌人能够增加关注,欢送斧正、交换。

争取每个知识点可能多总结一些,至多要做到在面试时,针对每个知识点都能够侃起来,不至于哑火。

前言

在上一篇文章【前端 · 面试】JavaScript 之你不肯定会的根底题(一)中,有同学产生了这样一个纳闷:为什么 click 事件的监听函数中,this.idevent.target.id 的输入值是不一样的?

明天咱们就来扒一扒这其中的原理。

题目

有如下的 HTML 文档构造:

<div id="parent">
    <div id="child" class="child">
        点我
    </div>
</div>

第一次执行如下 JavaScript 代码:

document.getElementById("parent").addEventListener("click", function () {alert(`parent 事件触发,` + this.id);
});

document.getElementById("child").addEventListener("click", function () {alert(`child 事件触发,` + this.id);
});

第二次执行另一套 JavaScript 代码:

document.getElementById("parent").addEventListener("click", function (e) {alert(`parent 事件触发,` + e.target.id);
});

document.getElementById("child").addEventListener("click", function (e) {alert(`child 事件触发,` + e.target.id);
});

问题如下:

点击 id 为 child 的 div 后,JavaScript 代码的执行后果别离是什么?

答案是:

  • 第一次后果为:先弹出“child 事件触发,child”,再弹出“parent 事件触发,parent”。
  • 第二次后果为:先弹出“child 事件触发,child”,再弹出“parent 事件触发,child”。

对于这个答案中的第二次输入后果,有人生出了纳闷:为什么 parent 事件触发时,e.target.id 的后果为 child 呢?不应该是 parent 吗?

解惑

DOM 元素事件执行程序

首先,咱们晓得,HTML 页面上 DOM 元素的事件执行程序个别有三个阶段:

  • 事件捕捉
  • 事件触发
  • 事件冒泡

整个过程如下图:

事件捕捉和事件冒泡

当一个事件产生在具备父元素的元素上 (例如,在咱们的例子中是 child 元素) 时,古代浏览器运行两个不同的阶段 – 捕捉阶段和冒泡阶段。在捕捉阶段:

  • 浏览器查看元素的最外层先人 <html>,是否在捕捉阶段中注册了一个onclick 事件处理程序,如果是,则运行它。
  • 而后,它挪动到 <html> 中单击元素的下一个先人元素,并执行雷同的操作,而后是单击元素再下一个先人元素,依此类推,直到达到理论点击的元素。

在冒泡阶段,恰恰相反:

  • 浏览器查看理论点击的元素是否在冒泡阶段中注册了一个 onclick 事件处理程序,如果是,则运行它
  • 而后它挪动到下一个间接的先人元素,并做同样的事件,而后是下一个,等等,直到它达到 <html> 元素。

这两个阶段如下图所示:

在古代浏览器中,默认状况下,所有事件处理程序都在 冒泡阶段进行注册,这也是为什么只有一个阻止冒泡办法的办法 event.stopPropagation(),而没有阻止捕捉的办法,因为齐全没必要。

this 和 event.target

首先,咱们得有一个清晰的认知:事件冒泡或者事件捕捉,都是针对注册了事件的元素。

对于 this 和 event.target,总结如下:

  • 在整个事件流程中,event.target 永远都指向真正触发了事件流程的元素,即处于事件触阶段的元素。
  • this 是正在执行事件的元素的援用,和 event.currentTarget 指向的元素是统一的,即以后执行的是哪个元素的监听事件,this 和 event.currentTarget 指向的就是哪个元素。

event 还有一个属性 event.srcElement,它是 event.target 的别名,然而是一个非标准属性,尽量不在生产环境中应用。

阻止冒泡

如果有以下代码:

parent.onclick = function1;
child.onclick = function2;

当咱们点击 child 时,因为事件默认会在冒泡阶段注册,所以,不仅会执行 function2,之后还会执行 function1,这样的后果可能不是咱们所冀望的,咱们更心愿它们的点击事件之间互不影响。

如果要实现这点,只须要在 function2 中增加 event.stopPropagation() 即可。

扩大

当初咱们将题目中的 JavaScript 代码再减少一份:

document.getElementById("parent").addEventListener("click", function (e) {alert(`parent 事件触发,` + e.target.id);
}, false);

document.getElementById("child").addEventListener("click", function (e) {alert(`child 事件触发,` + e.target.id);
}, true);

问题 1:如果点击 child 元素,输入是什么?

问题 2:如果点击 parent 元素,输入是什么?

能够看到,当初 parent 的点击事件是冒泡阶段执行,child 的点击事件是在 捕捉阶段执行。

针对问题 1,因为 parent 注册的是冒泡阶段执行,所以它的事件是在 child 触发阶段后的冒泡阶段执行的,所以答案应该是:先弹出“child 事件触发,child”,再弹出“parent 事件触发,child”。

针对问题二,尽管 child 注册的是捕捉阶段执行事件,然而 parent 事件流程的捕捉基本走不到它,所以答案应该是:只弹出“parent 事件触发,parent”。

总结

下面咱们剖析了这么多,其实总结起来就上面几条:

  • event.target 指向触发事件流程的元素,且不会扭转。
  • this 指向的是以后所执行事件的注册元素。
  • 捕捉止于 event.target,冒泡始于 event.target。
  • 支流浏览器都默认在冒泡阶段进行事件注册,所以,只有阻止冒泡的办法而没有阻止捕捉的办法。
  • 元素的 addEventListener 办法中的第三个参数是 true 或者 false,对元素本人触发的事件流程都没有任何影响,只有在它的父元素或者子元素在触发雷同的事件后才有影响。

小问题也有大本源,敢于发现,敢于探索!

~

~ 本文完,感激浏览!

~

学习乏味的常识,结识乏味的敌人,塑造乏味的灵魂!

大家好,我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢送关注,心愿大家多多指教!

你来,怀揣冀望,我有墨香相迎!你归,无论得失,唯以余韵相赠!

常识与技能并重,内力和外功兼修,实践和实际两手都要抓、两手都要硬!

退出移动版