DOM 事件詳解

墨夜_發表於2019-01-26

事件是 JavaScript 與 HTML 互動的基礎。要實現使用者與頁面的互動,先要對目標元素繫結特定的事件、設定事件處理函式,然後使用者觸發事件,事件處理函式執行,產生互動效果。

DOM 事件級別

DOM 級別分為四個級別:DOM0 級、DOM1 級、DOM2 級、DOM3 級;

DOM級別

DOM 事件級別分為三個級別:

  1. DOM0 級事件
<button id="btn" type="button"></button>
<script>
    var btn = document.getElementById('btn')
    btn.onclick = function() {
        console.log('Hello World')
    }
    // btn.onclick = null // 解綁事件
</script>
複製程式碼

缺點:無法設定多個事件處理函式

  1. DOM2 級事件
<button id="btn" type="button"></button>
<script>
    var btn = document.getElementById('btn');    
    btn.addEventListener('click', showFn, false)
    btn.addEventListener('click', showFn2, false)
    // btn.removeEventListener('click', showFn, false) // 解綁事件 
    function showFn() {
        alert('Hello World');
    }
     function showFn2() {
        alert('Hello World2');
    } 
</script>
複製程式碼

可以為事件設定多個事件處理函式,可以通過第三個引數 ( useCapture ) 設定在什麼階段執行事件處理函式,預設是 false, 即在事件冒泡階段執行事件處理函式。

需要注意的是在 IE8 及以下版本需要用 attachEventdetachEvent 實現,只有兩個引數,事件名需要以 on 開頭,只支援在事件冒泡階段執行事件處理函式。

  1. DOM3 級事件

DOM3 級事件是在 DOM2 級事件的基礎上新增了更多的事件型別,允許自定義事件。

  • UI事件,當使用者與頁面上的元素互動時觸發,如:load、scroll
  • 焦點事件,當元素獲得或失去焦點時觸發,如:blur、focus
  • 滑鼠事件,當使用者通過滑鼠在頁面執行操作時觸發如:dbclick、mouseup
  • 滾輪事件,當使用滑鼠滾輪或類似裝置時觸發,如:mousewheel
  • 文字事件,當在文件中輸入文字時觸發,如:textInput
  • 鍵盤事件,當使用者通過鍵盤在頁面上執行操作時觸發,如:keydown、keypress
  • 合成事件,當為IME(輸入法編輯器)輸入字元時觸發,如:compositionstart
  • 變動事件,當底層DOM結構發生變化時觸發,如:DOMsubtreeModified
// 自定義事件
var event = new Event('test')
// 給元素繫結事件
domElement.addEventListener('test', function() {
    console.log('event test')
},)

// 觸發事件
setTimeout(function() {
    domElement.dispatchEvent(event)
}, 1000)
複製程式碼

DOM 事件流

想象畫在一張紙上的一組同心圓。如果把手指放在圓心上,那麼手指指向的不僅僅是一個圓,而是紙上的所有圓。所以如果點選了某個按鈕,點選事件不僅僅發生在這個按鈕上,整個頁面也被點選了。

事件流又稱為事件傳播,描述的是從頁面中接收事件的順序。DOM2 級事件規定事件流包括三個階段: 事件捕獲(capturing phase)目標事件(target phase)事件冒泡(bubbling phase)
發生的順序是:事件捕獲階段 --> 目標事件階段 --> 事件冒泡階段

事件流

事件冒泡

事件開始時由最具體的元素(目標元素)接收,然後逐級向上傳播。

<style>
  #parent {
      width: 200px;
      height: 200px;
      background-color: green;
  }
  #child {
      width: 100px;
      height: 100px;
      background-color: yellow;
  }
</style>

<div id="parent">
  <div id="child">目標元素</div>
  父級元素
</div>

<script>
  var parent = document.getElementById('parent')
  var child = document.getElementById('child')
  
  parent.addEventListener('click', function(e) {
      console.log('parent bubbling')
  }, false)
  
  child.addEventListener('click', function() {
      console.log('target bubbling')
  }, false)
  
  document.body.addEventListener('click', function() {
      console.log('body bubbling')
  }, false)
  
  document.documentElement.addEventListener('click', function() {
      console.log('html bubbling')
  }, false)
  
  document.addEventListener('click', function() {
      console.log('document bubbling')
  }, false)
  
  window.addEventListener('click', function() {
      console.log('window bubbling')
  }, false)
</script>
複製程式碼

執行結果:

bubbling

事件捕獲

事件按 window -> document -> html -> body -> ... -> 目標元素 的方向向下層元素傳遞。

<style>
  #parent {
      width: 200px;
      height: 200px;
      background-color: green;
  }
  #child {
      width: 100px;
      height: 100px;
      background-color: yellow;
  }
</style>

<div id="parent">
  <div id="child">目標元素</div>
  父級元素
</div>

<script>
  var parent = document.getElementById('parent')
  var child = document.getElementById('child')
  
  parent.addEventListener('click', function(e) {
      console.log('parent capture')
  }, true)
  
  child.addEventListener('click', function() {
      console.log('target capture')
  }, true)
  
  document.body.addEventListener('click', function() {
      console.log('body capture')
  }, true)
  
  document.documentElement.addEventListener('click', function() {
      console.log('html capture')
  }, true)
  
  document.addEventListener('click', function() {
      console.log('document capture')
  }, true)
  
  window.addEventListener('click', function() {
      console.log('window capture')
  }, true)
</script>
複製程式碼

執行結果:

bubbling

事件物件引數 event

在使用者觸發事件,執行事件處理函式的時候,預設會向事件處理函式傳入一個 event 物件,它記錄了該事件的狀態和行為。

event 常用屬性和方法

  • type 事件型別
  • target 事件發出者(觸發事件的元素)
  • currentTarget 事件監聽者(被繫結事件的元素)
  • stopPropagation() 阻止事件冒泡或捕獲
  • preventDefault() 阻止瀏覽器預設行為

target 、currentTarget 與 this

<div id="parent">
    <div id="child"></div>
</div>
<script>
    var parent = document.getElementById('parent')
    
    function handler(e) {
        console.log(e.target)
        console.log(e.currentTarget)
        console.log(this)
    }
    // 給父盒子註冊點選事件
    parent.addEventListener('click', handler, false)
</script>
複製程式碼

當點選 parent 時,輸出:

1 <div id="parent">...</div>
2 <div id="parent">...</div>
3 <div id="parent">...</div>
複製程式碼

當點選 child 時,輸出:

1 <div id="child">...</div>
2 <div id="parent">...</div>
3 <div id="parent">...</div>
複製程式碼

所以 target 是事件發出者,curentTarget 是事件監聽者,事件處理函式中的 this 等同於 e.currentTarget

event 物件的一些相容性寫法

  • 獲得 event
// 事件處理函式
function handleClick(event) {
    var e = event || window.event
    ···
}
複製程式碼
  • 獲得 target
···
var target = e.target || e.srcElement
···
複製程式碼
  • 阻止瀏覽器預設行為
···
e.preventDefault ? e.preventDefault() : (e.returnValue = false)
···
複製程式碼
  • 阻止冒泡
···
e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true)
···
複製程式碼
  • 事件繫結與解綁
function addEvent(element, type, fn) {
    element.addEventListener ? element.addEventListener(type, fn, false) : element.attachEvent('on'+ type, fn)
}

function removeEvent(element, type, fn) {
    element.removeEventListener ? element.removeEventListener(type, fn, false) : element.detachEvent('on'+ type, fn)
}
複製程式碼

屬性表

  • 基礎屬性
屬性 描述
altKey 返回當事件被觸發時,”ALT” 是否被按下。
button 返回當事件被觸發時,哪個滑鼠按鈕被點選。
clientX 返回當事件被觸發時,滑鼠指標的水平座標。
clientY 返回當事件被觸發時,滑鼠指標的垂直座標。
ctrlKey 返回當事件被觸發時,”CTRL” 鍵是否被按下。
metaKey 返回當事件被觸發時,”meta” 鍵是否被按下。
relatedTarget 返回與事件的目標節點相關的節點。
screenX 返回當某個事件被觸發時,滑鼠指標的水平座標。
screenY 返回當某個事件被觸發時,滑鼠指標的垂直座標。
shiftKey 返回當事件被觸發時,”SHIFT” 鍵是否被按下。
  • IE 屬性
屬性 描述
cancelBubble 如果事件控制程式碼想阻止事件傳播到包容物件,必須把該屬性設為 true。
fromElement 對於 mouseover 和 mouseout 事件,fromElement 引用移出滑鼠的元素。
keyCode 對於 keypress 事件,該屬性宣告瞭被敲擊的鍵生成的 Unicode 字元碼。對於 keydown 和 keyup
offsetX,offsetY 發生事件的地點在事件源元素的座標系統中的 x 座標和 y 座標。
returnValue 如果設定了該屬性,它的值比事件控制程式碼的返回值優先順序高。把這個屬性設定為 false 可以阻止瀏覽器預設行為
srcElement 對於生成事件的 Window 物件、Document 物件或 Element 物件的引用。
toElement 對於 mouseover 和 mouseout 事件,該屬性引用移入滑鼠的元素。
x,y 事件發生的位置的 x 座標和 y 座標,它們相對於用CSS動態定位的最內層包容元素。
  • 標準 event 屬性(2級 DOM 事件標準定義的屬性)
屬性或方法 描述
bubbles 返回布林值,指示事件是否是冒泡事件型別。
cancelable 返回布林值,指示事件是否可擁可取消的預設動作。
currentTarget 返回其事件監聽器觸發該事件的元素。
eventPhase 返回事件傳播的當前階段。
target 返回觸發此事件的元素(事件的目標節點)。
timeStamp 返回事件生成的日期和時間。
type 返回當前 Event 物件表示的事件的名稱。
initEvent() 初始化新建立的 Event 物件的屬性。
preventDefault() 通知瀏覽器不要執行與事件關聯的預設動作。
stopPropagation() 不再派發事件(常用於阻止事件冒泡)。

閱讀原文
參考:# 事件流理解 、#javascript event(事件物件)詳解 、# DOM 事件深入淺出(一)

相關文章