【每日一題NO.54】說(shuō)下你對(duì)時(shí)間流的理解

人生苦短,總需要一點(diǎn)儀式感。比如學(xué)前端~
目錄:
事件
事件流
事件冒泡、事件捕獲
DOM 事件處理
DOM0
DOM2
事件
JavaScript 和 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的。
事件就是通過(guò)文檔或?yàn)g覽器窗口發(fā)生的一些特定的交互瞬間。
可以使用監(jiān)聽(tīng)器 (或者事件處理程序)來(lái)預(yù)定事件,以便事件發(fā)生時(shí)執(zhí)行相應(yīng)的代碼。
事件流
描述的是頁(yè)面中事件傳播的順序。
當(dāng)事件發(fā)生的時(shí)候,會(huì)在元素節(jié)點(diǎn)之間按照特定的順序傳播,這個(gè)傳播的過(guò)程叫做 DOM 的事件流。
這個(gè)傳播過(guò)程是:事件捕獲 -- 事件目標(biāo) -- 事件冒泡。
而早期的?微軟IE 和 網(wǎng)景NetScape 提出了完全相反的事件流概念:IE 是事件冒泡;而 NetScape 的事件流就是事件捕獲。
事件冒泡、事件捕獲
IE 提出的事件流是事件冒泡,即從下到上,從目標(biāo)觸發(fā)的元素逐級(jí)自下向上傳播,直到 window 對(duì)象。 而 NetScape 的事件流就是事件捕獲,即從 document 對(duì)象逐級(jí)向下傳播到最底端的目標(biāo)元素。由于 IE 低版本瀏覽器不支持,所以很少使用事件捕獲。 后來(lái) ECMAScript 在 DOM2 中對(duì)事件流進(jìn)行了進(jìn)一步規(guī)范,基本上就是上述二者的結(jié)合。
DOM2 事件規(guī)定的事件流包括三個(gè)階段:
事件捕獲階段:事件從Document節(jié)點(diǎn)自上而下向目標(biāo)節(jié)點(diǎn)傳播的階段 處于目標(biāo)階段:真正的目標(biāo)節(jié)點(diǎn)正在處理事件的階段 事件冒泡階段:事件從目標(biāo)節(jié)點(diǎn)自下而上向Document傳播的階段
DOM 事件處理
DOM 節(jié)點(diǎn)增加了對(duì)事件處理,其分為四個(gè)級(jí)別
DOM0
DOM0級(jí)事件具有極好的跨瀏覽器優(yōu)勢(shì),會(huì)以最大的速度綁定
第一種方式是內(nèi)聯(lián)模型【行內(nèi)模式】
<div?onclick="btnClick()">clickdiv>
<script>
??function?btnClick() {
????console.log("hello");
??}
script>
缺點(diǎn)是:不符合 W3C 中關(guān)于內(nèi)容與行為分離的基本規(guī)范
第二種方式是腳本模式
<div?id="btn">點(diǎn)擊div>
<script>
??var?btn = document.getElementById("btn");
??btn.onclick = function() {
????console.log("小石頭");
??}
script>
但是會(huì)存在問(wèn)題:
<div?id="btn">點(diǎn)擊div>
<script>
??var?btn = document.getElementById("btn");
??btn.onclick = function(){
????console.log("小石頭");
??}
??btn.onclick = function(){
????console.log("石頭姐");
??}
script>
多次綁定時(shí),這個(gè)時(shí)候只能輸出 石頭姐。很明顯的是:第一個(gè)事件函數(shù)被第二個(gè)事件函數(shù)給覆蓋掉了。
所以腳本模型的缺點(diǎn)就是:同一個(gè)節(jié)點(diǎn)只能添加一次同類型事件
<div?id="btn3">
??btn3
??<div?id="btn2">
????btn2
????<div?id="btn1">
??????btn1
????div>
??div>
div>
<script>
??let?btn1?=?document.getElementById("btn1");
??let?btn2?=?document.getElementById("btn2");
??let?btn3?=?document.getElementById("btn3");
??btn1.onclick = function(){
??????console.log(1)
??}
??btn2.onclick = function(){
??????console.log(2)
??}
??btn3.onclick = function(){
??????console.log(3)
??}
script>
當(dāng)點(diǎn)擊?div#btn1 的時(shí)候,會(huì)依次輸入 1,2,3,這就是事件冒泡,DOM0 級(jí)只支持冒泡階段
DOM2
DOM2定義了兩個(gè)方法:
addEventListener 添加事件偵聽(tīng)器 removeEventListener 刪除事件偵聽(tīng)器
這兩個(gè)方法均有三個(gè)參數(shù):
第一個(gè)參數(shù)是要處理的事件名稱 第二個(gè)參數(shù)是作為事件處理程序的函數(shù)名稱 第三個(gè)參數(shù)是一個(gè)布爾值,默認(rèn)為false。false表示使用冒泡機(jī)制,true表示使用捕獲機(jī)制
<div?id="btn">點(diǎn)擊div>
<script>
var?btn = document.getElementById("btn");
btn.addEventListener("click", helloFun, false);
btn.addEventListener("click", helloAgainFun, false);
function?helloFun(){
????console.log("hello");
}
function?helloAgainFun(){
????console.log("hello?again");
}
script>
捕獲冒泡的順序
<div?id="btn3">
????btn3
????<div?id="btn2">
????????btn2
????????<div?id="btn1">
????????????btn1
????????div>
????div>
div>
<script>
????let?btn1?=?document.getElementById("btn1");
????let?btn2?=?document.getElementById("btn2");
????let?btn3?=?document.getElementById("btn3");
????btn1.addEventListener('click',?function?()?{
????????console.log('btn1捕獲')
????},?true)
????btn1.addEventListener('click',?function?()?{
????????console.log('btn1冒泡')
????},?false)
????btn2.addEventListener('click',?function?()?{
????????console.log('btn2捕獲')
????},?true)
????btn2.addEventListener('click',?function?()?{
????????console.log('btn2冒泡')
????},?false)
????btn3.addEventListener('click',?function?()?{
????????console.log('btn3捕獲')
????},?true)
????btn3.addEventListener('click',?function?()?{
????????console.log('btn3冒泡')
????},?false)
script>
點(diǎn)擊btn1,其結(jié)果為:
btn3捕獲 - btn2捕獲 - btn1捕獲 - btn1冒泡 - btn2冒泡 - btn3冒泡
阻止冒泡 event.stopPropagation()
btn2.addEventListener(
??"click",
??function?(ev)?{
????ev.stopPropagation();
????console.log("btn2捕獲");
??},
??true
);所有《每日一題》的 知識(shí)大綱索引腦圖 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以點(diǎn)擊文末的 “閱讀原文” 快速跳轉(zhuǎn)

