DOM事件模型
事件
參考:
原生JavaScript和Vue、小程式都是如何阻止事件冒泡的?
DOM事件級別
1 事件流
事件流描述的是從頁面中接受事件的順序。
1.1 事件冒泡
IE 的事件流叫事件冒泡,即事件開始時由最具體的元素接受,然後逐級向上傳播到較為不具體的節點(文件),Chrome,Firefox和Safari會將事件一直冒泡到 window 物件。
1.2 事件捕獲
Netscape Communicator 團隊提出的另一種事件流叫做事件捕獲。事件捕獲的思想是不太具體的節點應該更早接受到事件,而最具體的節點應該最後接收到事件。事件捕獲的用以在於事件到達預定目標之前捕獲它。一般從document物件開始傳播,用的較少。
1.3 DOM事件流
上圖是W3C標準的DOM事件流模型圖。
“DOM2級事件” 規定的事件流包括三個階段:事件捕獲階段、處於目標階段、事件冒泡階段。
2 事件處理程式
前面說的是事件,事件就是使用者或瀏覽器自身執行的某種動作。如:click是事件的名字,事件處理程式就是onclick。為事件指定處理程式的方式有好幾種
2.1 HTML 事件處理程式
其實就是,HTML 的on屬性
<button onclick="doxxx()">點選</button>
使用這個方法指定的監聽程式碼只會在冒泡階段觸發。
2.2 DOM0 級事件處理程式,缺點:無法設定多個事件處理函式
元素節點的事件屬性。
let btn = document.getElementById("myBtn:); btn.onclick = function () { alert("Clickes") }
如果想刪除只需:btn.onclick = null; // 刪除事件處理程式
2.3 DOM2 級事件處理程式,可以為事件設定多個事件處理函式,可以通過第三個引數 ( useCapture ) 設定在什麼階段執行事件處理函式,預設是 false, 即在事件冒泡階段執行事件處理函式。
DOM2級事件定義了兩個方法,用於處理指定 和 刪除事件處理程式的操作:addEventListener()
所有DOM節點中都包含這兩種方法。並且都接受三個引數:要處理的事件名,作為事件處理程式的函式,一個布林值(預設false表冒泡)。
(1)EventTarget.addEventListener()
function print() { console.log('Print Hello world'); } var btn = document.getElementById('btn'); btn.addEventListener('click', print, false);
上面程式碼的意思就是,節點id為btn的元素,使用addEventListener方法繫結click事件,點選的時候會監聽函式print會發生。另外,因為useCapture設定為false,所以該函式只在冒泡階段觸發。
當我們需要新增多個不同的監聽函式,使用addEventListener非常棒,遵從先新增先觸發原則,而且不小心在同一個事件重複添加了同一個函式,它還會自動移除,該函式只會執行一次。
(2)EventTarget.removeEventListener()
顧名思義,用來移除addEventListener方法新增的事件監聽函式。
它的引數和addEventListener方法一致,而且寫的時候要一一對應。要做到三同,一是在同一個元素節點,二是同一個監聽函式,三是第三個引數要一致。
2.4 IE 事件處理程式
(1)事件繫結監聽函式:attachEvent(eventType, listener)
(2)事件移除監聽函式:detachEvent(eventType, listener)
3 事件物件
3.1 DOM 中的事件物件
事件發生的時候,會將一個 event 物件傳入到事件處理程式中,從而可以使用物件的屬性和方法。我隨意列出幾個。bubbles:返回一個布林值,表示當前事件是否會冒泡。該屬性為只讀屬性
cancelable:返回一個布林值,表示事件是否可以取消。該屬性為只讀屬性
currentTarget:事件處理程式當前正在處理事件的那個元素
preventDefault():取消事件的預設行為。如果 cancelable 是true,則可以使用這個方法
stopPropagation():取消事件的進一步捕獲或冒泡。如果 bubbles為 true,則可以使用這個方法。
事件處理程式使用DOM0 級或是 DOM2 級,都會傳入 event 物件,這個我們不用care。
var btn = document.getElementById("myBtn"); btn.onclick = function(event){ console.log(event.type); //"click" }; btn.addEventListener("click", function(event){ console.log(event.type); //"click" }, false); <button onclick="console.log(event.type)()">點選</button>
3.2 IE中的事件物件
與訪問DOM中的event物件不同,要訪問IE中的event物件有幾種不同的方式,取決於指定事件處理程式的方法。
(1) HTML 事件處理程式
<button onclick="console.log(event.type)()">點選</button>
(2) DOM0 級事件處理程式
let btn = document.getElementById("btn"); btn.onclick = function(){ var event = window.event; console.log(event.type); //"click" };
(3) DOM2 級事件處理程式
let btn = document.getElementById("btn"); btn.attachEvent("onclick", function(event){ console.log(event.type); //"click" });
4 事件型別(DMO3 級事件規定了以下幾類事件)
DOM3 級事件是在 DOM2 級事件的基礎上添加了更多的事件型別,允許自定義事件。
- UI事件,當用戶與頁面上的元素互動時觸發,如:load、scroll
- 焦點事件,當元素獲得或失去焦點時觸發,如:blur、focus
- 滑鼠事件,當用戶通過滑鼠在頁面執行操作時觸發如:dbclick、mouseup
- 滾輪事件,當使用滑鼠滾輪或類似裝置時觸發,如:mousewheel
- 文字事件,當在文件中輸入文字時觸發,如:textInput
- 鍵盤事件,當用戶通過鍵盤在頁面上執行操作時觸發,如:keydown、keypress
- 合成事件,當為IME(輸入法編輯器)輸入字元時觸發,如:compositionstart
- 變動事件,當底層DOM結構發生變化時觸發,如:DOMsubtreeModified
如何阻止冒泡
例子:
<div onclick="alert('最外層')">
<div onclick="alert('中間層')">
<a id="ahref" href="http://www.javanx.cn" onclick="alert('最裡層')">點選我</a>
</div>
</div>
點選:點選我 --》最裡層 --》中間層 --》最外層 --》然後轉跳連結。這就是冒泡。
1.JavaScript 中
event.stopPropagation() // propagation: 傳播$(function() { $("#ahref").click(function(event) { event.stopPropagation(); }); });
點選:點選我 --》 最裡層 --》轉跳連結 事件處理過程中,阻止了事件冒泡,但不會阻擊預設行為(執行了超連結的跳轉)
return false
$(function() { $("#ahref").click(function(event) { return false; }); });
點選:點選我 --》 最裡層 --》但不會轉跳連結事件處理過程中,阻止了事件冒泡,也阻止了預設行為(沒有執行超連結的跳轉)
event.preventDefault()
$(function() { $("#ahref").click(function(event) { event.preventDefault() }); });
點選:最裡層 --》中間層 --》最外層,但最後沒有轉跳 不能阻止事件冒泡,但阻止了預設行為。
2.Vue 解決事件冒泡
Vue.js 提供了事件修飾符,我們只需要新增 click.stop 即可預防事件冒泡
<div @click="cancelSelect"> <div class="picker_alert" @click.stop> <div class="ofh"> <span @click="toRoomManagement">編輯</span> <span @click="confirmRoom">確定</span> </div> </div> </div>
Vue修飾符:.stop, .prevent, .capture, .self, .once
<!-- 阻止單擊事件冒泡 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再過載頁面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修飾符可以串聯 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修飾符 --> <form v-on:submit.prevent></form> <!-- 新增事件偵聽器時使用事件捕獲模式 --> <div v-on:click.capture="doThis">...</div> <!-- 只當事件在該元素本身(比如不是子元素)觸發時觸發回撥 --> <div v-on:click.self="doThat">...</div>
提示:使用修飾符時,順序很重要;相應的程式碼會以同樣的順序產生。
因此,用 @click.prevent.self 會阻止所有的點選,而 @click.self.prevent 只會阻止元素上的點選。