DOM事件處理機制學習筆記

来源:https://www.cnblogs.com/chinaontology/archive/2022/05/02/16213592.html
-Advertisement-
Play Games

DOM 事件是處理 Web 頁面交互的基礎,是掌握前端開發技術的基礎。 W3C協會早在1988年就開始了DOM標準的制定,W3C DOM標準可以分為DOM1,DOM2,DOM3三個版本。 1.Html事件處理 原始事件模型,事件處理程式被設置為html控制項的性質值,一般是html控制項的 onclic ...


DOM 事件是處理 Web 頁面交互的基礎,是掌握前端開發技術的基礎。
W3C協會早在1988年就開始了DOM標準的制定,W3C DOM標準可以分為DOM1,DOM2,DOM3三個版本。

1.Html事件處理

原始事件模型,事件處理程式被設置為html控制項的性質值,一般是html控制項的 onclick、onerror、onload ....
如:

<button type="button" onclcik="console.log('Hello Console!')">ShowConsole</button>
<button type="button" onclick="showFn()">ShowFn</button>
<script>
    function showFn() {
        alert('Hello World');
    }
</script>

優點:
簡單快速,此時代碼的作用域是文檔全局,可以直接使用文檔所有公共變數

缺點:
1、是HTML於JS 強耦合,綁定在一起
2、一個處理程式無法同時綁定多個處理函數
3、執行時機問題:可能會出現 html 上已經顯示控制項,但是關聯的函數還未載入進來,此時用戶點擊,就會執行失敗(你沒辦法告知用戶要等一會兒...)
4、不同瀏覽器對此方式綁定的函數的作用域解釋可能有差異,在部分情況下會導致不同的行為

2.DOM0級事件

就是先取得控制項變數,然後給變數的事件處理程式賦予一個函數,解綁事件處理是將事件處理程式賦為 null

<input id="x1" type="text" value="World!">
<script>
    var x1 = document.getElementById('x1'); 
    x1.onclick = function() {
        alert('Hello World'+this.value);
    } 
    // x1.onclick = null; 解綁事件 
</script>

特點

  • 此方式添加的代碼是註冊在冒泡階段的
  • 此方式添加的函數是元素的屬性,作用域是元素範圍,可以引用元素的屬性(使用 this 代表元素本身)

3.DOM2級事件

DOM級別1於1998年10月1日成為W3C推薦標準。1級DOM標準中並沒有定義事件相關的內容,所以沒有所謂的1級DOM事件模型。

在2級DOM中除了定義了一些DOM相關的操作之外還定義了一個事件模型。當事件發生在節點時,目標元素的事件處理函數就被觸發,而且目標的每個祖先節點也有機會處理那個事件。

DOM事件流包括兩個階段:
1.capturing 消息捕獲階段,事件從Document對象沿著文檔樹向下傳播給節點。如果目標的任何一個祖先專門註冊了事件監聽函數,那麼在事件傳播的過程中就會運行這些函數。
2.下一個階段發生在目標節點自身,直接註冊在目標上的適合的事件監聽函數將運行。
3.bubbling 消息冒泡階段,這個階段事件將從目標元素向上傳播回Document對象(與capturing相反的階段)。雖然所有事件都受capturing階段的支配,但並不是所有類型的事件都bubbling。(0級DOM事件模型處理沒有capturing階段)

老版本的瀏覽器不支持消息捕獲階段的處理。一般都只需要處理消息冒泡階段的事件。

如果想在事件發生前捕獲,然後阻止預設動作發生,可以指定事件處理程式在捕獲階段運行。

所謂事件冒泡就是事件像泡泡一樣從最開始生成的地方一層一層往上冒,一層一層向上直至最外層的html或document。

<div id="box">
    <a id="child">事件冒泡</a>
</div>
<script>
 var box = document.getElementById('box'),
 child = document.getElementById('child');
 
 child.addEventListener('click', function() {
 alert('我是目標事件');
 }, false);
 
 box.addEventListener('click', function() {
 alert('事件冒泡至DIV');
 }, false);
</script>

上面的代碼運行後我們點擊a標簽,首先會彈出’我是目標事件’提示,然後又會彈出’事件冒泡至DIV’的提示,這便說明瞭事件自內而外向上冒泡了。

DOM2級標準中,事件定義了addEventListener和removeEventListener兩個方法,分別用來綁定和解綁事件。

方法中包含3個參數,分別是:
(1)綁定的事件處理屬性名稱(不包含on)
(2)處理函數
(3)是否在捕獲時執行,省略此參數時,預設為false,表示僅在冒泡階段處理

DOM2級事件允許給一個處理程式添加多個處理函數。代碼如下:

<button id="btn" type="button"></button>
<script>
    var btn = document.getElementById('btn');
    function showFn() {
        alert('Hello World');
    }
    btn.addEventListener('click', showFn, false);
    btn.addEventListener('mouseover', showFn, false); // 添加一個滑鼠移入的方法
    // btn.removeEventListener('click', showFn, false); 解綁事件 
</script>

如果是 IE8 以下需要用attachEvent和detachEvent來實現

btn.attachEvent('onclick', showFn); // 綁定事件 
btn.detachEvent('onclick', showFn); // 解綁事件
\\ 這裡我們不需要傳入第三個參數,因為IE8級以下版本只支持冒泡型事件。

4.事件對象

DOM當中發生事件時,所有信息都會被收集並存儲到一個event對象當中
可以在調試台查看 event 對象的結構

let btn = document.getElementById('mybtn');
btn.onclick = function(event){
  console.log(event);
}
btn.addEventListener("click",(event)=>{
  console.log(event);
  // event.stopPropagation();  
  // 加上上面這一句,則按鈕以上層次的就監聽不到 click 事件了,事件冒泡被終止
});
document.body.onclick = (event)=>{
  console.log(event);
}

event對象有一些重要屬性,以下是必須牢記的:

  • type 代表事件類型,例如:click、mouseover...
  • target 代表事件的目標控制項
  • currentTarget 代表事件監聽者控制項,等於 this
  • eventPhase 事件處理程式的階段:1.捕獲 2.到達目標 3.冒泡階段
  • shiftKey bool類型,發生事件時 shift 鍵是否按下
  • ctrlKey bool類型,發生事件時 ctrl 鍵是否按下
  • altKey bool類型,發生事件時 Alt 鍵是否按下
    還有一些方法:
  • stopPropagation 方法:取消事件冒泡
  • preventDefault 方法:阻止預設事件的發生

在上面的代碼當中,點擊按鈕 btn 監聽click 事件,輸出的結果中 event 的 target、currentTarget 都等於 this,就是按鈕對象自身;
但在頁面的 body 的監聽當中,接收到的 event 當中,currentTarget 和 this 代表 body,target 當表點擊發生的真正對象 btn

// 採用 preventDefault 方法,阻止超鏈接的預設動作
let link = document.getElementById("mylink");
link.onclick = (event){
  // 此處可以加上 用戶狀態判斷...
  event.preventDefault();
}

採用 event 的 type 屬性,可以簡化事件處理函數

let btn = document.getElementById("mybtn");
let handler = (event)=>{
  switch(event.type){
    case "click":
      console.log("clicked");
      break;
    case "mouseover":
      console.log("mouseover");
      break;
    default:
      console.log(event.type);
      break;
  }
}
btn.onclick = handler;
btn.onmouseover = handler;

5.DOM3級事件

DOM3級事件在DOM2級事件的基礎上添加了更多的事件類型,全部類型如下:

UI事件,當用戶與頁面上的元素交互時觸發,如:load、unload、scroll
焦點事件,當元素獲得或失去焦點時觸發,如:blur、focus
滑鼠事件,當用戶通過滑鼠在頁面執行操作時觸發如:dbclick、mouseup
滾輪事件,當使用滑鼠滾輪或類似設備時觸發,如:mousewheel
文本事件,當在文檔中輸入文本時觸發,如:textInput
鍵盤事件,當用戶通過鍵盤在頁面上執行操作時觸發,如:keydown、keypress
合成事件,當為IME(輸入法編輯器)輸入字元時觸發,如:compositionstart,compositionupdate,compositionend
變動事件,當底層DOM結構發生變化時觸發,如:DOMsubtreeModified
同時DOM3級事件也允許使用者自定義一些事件。

一些常用的事件:

// load:頁面載入事件
window.addEventListener("load",(event)=>{
  console.log("html文檔已經載入完畢");      
});

// DOMContentLoaded: 網頁DOM樹構建完成後觸發,並不等待圖像等附加資源完成

// beforeunload:可以發生在網頁內容卸載之前,可以阻止(例如要求用戶進行保存)
window.addEventListener("beforeunload",(event)=>{
  console.log("文檔保存...");  
});

// unload:可以發生在網頁導航,載入新網頁時, 舊內容已經卸載
window.addEventListener("unload",(event)=>{
  console.log("文檔已經卸載...");  
});

// error:添加全局錯誤的監聽,如果頁面內圖像未載入成功,就將圖像替換為另一張圖像
window.addEventListener("error",(event)=>{
          console.log('body knows:',event);
      console.log(event.target.tagName);
      if (event.target.tagName=="IMG"){
          console.log(event.target);
          event.target.src = 'img/404.jpg';
      }
  },true);

// resize:視窗縮放事件
window.addEventListener("resize",(event)=>{
        console.log("height=",window.innerHeight);
    console.log("width=",window.innerWidth);
});

// textInput:監聽輸入內容
// textInput 不同於 keyPress,只監聽輸入區內容,且可以通過 event.data 獲得輸入的內容
let myTxt = document.getElementById("myInput");
    myTxt.addEventListener("textInput",(event)=>{
    console.log(event.data);
    if (event.data=="yyds"){
        event.target.value = event.target.value.replace(event.data,"永遠的神");
    }
});

6.時鐘事件

setTimeout函數,可以利用系統時鐘,創建一個被時鐘喚醒的函數,來執行功能;
setInterval函數,可以利用系統時鐘,創建一個以固定間隔執行任務的程式。

// 創建一個5秒鐘以後執行的程式
let timer1 = setTimeout(()=>{ console.log('一次性任務');},5000);
// 如果在timer1執行前調用此函數,可以取消時鐘任務
function killTimer(){
    clearTimeout(timer1);
}

// 創建每秒報時的報時器
let timer2 = setInterval(()=>{
  console.log(new Date());
},1000);
// 清除報時器
function killInteTimer(){
   clearTimeout(timer2);
}

7.SPA應用中的事件處理框架

為了避免在html控制項上直接添加事件處理程式與監聽程式,避免複雜凌亂的事件函數,避免html與JS代碼的混合,以及避免執行時機、記憶體泄露等問題,
可以利用 DOM 事件機制,利用事件捕獲以及事件冒泡,在 DOM 的頂層節點上添加事件監聽處理函數,將用戶交互事件處理入口歸併到一處。
主要原理是,在需要交互的控制項上添加自定義屬性,並賦值,在DOM頂層監聽中,利用截獲的 event.target 獲得事件發生的控制項,
利用控制項的 attributes 數組獲得自定義屬性的值,以這些值作為參數,用於事件處理函數。

// 以下程式中,控制項上添加了 ele_type 自定義屬性
// Html控制項  
<span id="lab_username" ele_type="username" ele_data="Bruce">精思入神</button>
<p ele_type="userinfo">這個用戶剛剛註冊,還沒有填寫個人信息</p>

// 統一的處理入口
document.addEventListener('click',function(event){
    let t = event.target;
    if (!t.attributes.ele_type){
        return;
    }
    let type = t.attributes.ele_type.value;
    if (type=="username"){
        console.log("username clicked!")
        let userid = t.attributes.ele_data.value;
        console.log(userid); // 輸出 Bruce
    }
    if (type=="userinfo"){
        console.log("userinfo clicked!")
    }
})

8.事件模擬

可以利用代碼模擬事件。可以用在程式邏輯需要的地方,不需要用戶動作,自動提供事件

let btn = document.getElementById("mybtn");
// 創建事件
let m_event = document.createEvent("MouseEvents");
// 初始化為滑鼠click事件
m_event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
// 通過 btn 按鈕發送出去(此 event 的target自動賦值為當前按鈕)
btn.dispatchEvent(m_event);

9.用戶自定義事件

利用用戶自定義事件,可以在控制項、模塊之間傳遞消息,不用在模塊之間直接函數調用,可以讓相互調用更加靈活。

// 自定義事件
let div = document.getElementById('myInput');
// 定義用戶自定義事件監聽程式,app_event 是自己定義的全新消息類型
div.addEventListener("app_event",(event)=>{
    console.log(event.detail);
});
// 激發用戶自定義事件
function sendAppEvent(){
    if (document.implementation.hasFeature("CustomEvents","3.0")){
        event = document.createEvent("CustomEvent");
        event.initCustomEvent('app_event',true,false,'Hello App!');
        div.dispatchEvent(event);
    }
}

參考文獻:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基於 OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)源碼寫點內容,幫助大家瞭解下協議開發領域,儘可能將 3gpp 協議內容與 OpenHarmony 電話子系統模塊進行結合講解。 ...
  • 變聲是直播類、聊天類應用中用戶經常使用的功能。例如:很多主播選擇使用變聲器來實現帶動直播間氣氛;和朋友語音聊天時選擇變成蘿莉音讓聊天更有趣。HMS Core音頻編輯服務提供變聲能力,幫助開發者在應用中構建變聲功能。用戶可以通過預置的變聲風格進行變聲,提升音頻可玩性的同時有效保護用戶隱私,讓你隨心所欲 ...
  • 技術大咖們從開源實戰項目總結經驗,利用真實場景的應用案例分享前沿技術,引導開發者從零參與 OpenHarmony 開源貢獻,提升代碼效率,培養開發者成為開源社區的貢獻者。 ...
  • 1. 準備階段 關於該功能的實現我們需要學習以下的資料: 1.1 【ARKUI】ets怎麼實現文件操作 1.2 文件管理 1.3 Ability上下文 2. demo 實現 2.1 文件路徑讀取 參考 context.getFilesDir 來進行獲取文件路徑,代碼如下 private getCac ...
  • 今天做了一個案例,可以好好做做能夠將之前的內容結合起來,最主要的是能對組件化編碼流程有一個大概的清晰認知,這一套做下來,明天自己再做一遍複習一下,其實組件化流程倒是基本上沒什麼問題了,主要是很多vue的方法需要多熟悉一下,畢竟打破了之前的一些對於傳統js的認知,還需要多熟悉一下。 這兩天可能內容不是 ...
  • 大家好,我是半夏👴,一個剛剛開始寫文的沙雕程式員.如果喜歡我的文章,可以關註➕ 點贊 👍 加我微信:frontendpicker,一起學習交流前端,成為更優秀的工程師~關註公眾號:搞前端的半夏,瞭解更多前端知識! 點我探索新世界! 原文鏈接 ==>http://sylblog.xin/archi ...
  • 一、主要區別 1、{} 和 new Object() 除了本身創建的對象,都繼承了 Object 原型鏈上(Object.prototype)的屬性或者方法,eg:toString();當創建的對象相同時,可以說 {} 等價於 new Object() 。2、Object.create() 是將創建 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. 非同步編程的實現方式? JavaScript中的非同步機制可以分為以下幾種: 回調函數 的方式,使用回調函數的方式有一個缺點是,多個回調函數嵌套的時候會造成回調函數地獄,上下兩層的回調函數間的代碼耦合度太高,不利於代碼的可維護。 Pro ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...