1 DOM 事件模型(DOM 事件機(jī)制)
HTML DOM 允許 JavaScript 對(duì) HTML 事件作出反應(yīng)续担。JavaScript 能夠在事件發(fā)生時(shí)執(zhí)行暴构,比如當(dāng)用戶(hù)點(diǎn)擊某個(gè) HTML 元素時(shí)麻敌。JavaScript與HTML之間的交互是通過(guò)事件實(shí)現(xiàn)的诲宇。
事件由事件源孩革、事件類(lèi)型和事件處理程序三部分組成
事件執(zhí)行步驟:獲取事件源 => 注冊(cè)(綁定)事件 => 采用函數(shù)賦值形式添加事件處理程序
2 事件流
事件流描述了頁(yè)面接受事件的順序岁歉,IE 和 Netscape 開(kāi)發(fā)團(tuán)隊(duì)提出了幾乎完全相反的事件流方案,IE支持事件冒泡流膝蜈,Netscape 支持事件捕獲流锅移。2002年,W3C發(fā)布標(biāo)準(zhǔn)饱搏,規(guī)定瀏覽器應(yīng)該支持兩種調(diào)用方式非剃,開(kāi)發(fā)者自己選擇把函數(shù)放在捕獲階段還是冒泡階段。
2.1 事件捕獲
Netscape事件流被稱(chēng)作事件捕獲流推沸,規(guī)定為從外向內(nèi)找監(jiān)聽(tīng)函數(shù)的過(guò)程备绽。
過(guò)程實(shí)例:當(dāng)點(diǎn)擊div元素時(shí)券坞,觸發(fā)click事件順序:
document => html => body => div
在事件捕獲中,click事件首先由document元素捕獲疯坤,隨后沿著DOM樹(shù)向下傳播报慕,直到到達(dá)目標(biāo)元素。
由于舊版本瀏覽器不支持压怠,因此通常建議使用事件冒泡眠冈,特殊情況使用時(shí)間捕獲。
2.2 事件冒泡
IE事件流被稱(chēng)作事件冒泡流菌瘫,規(guī)定為從內(nèi)向外找監(jiān)聽(tīng)函數(shù)的過(guò)程蜗顽。
過(guò)程實(shí)例:當(dāng)點(diǎn)擊div元素時(shí),觸發(fā)click事件順序:
div => body => html => document
在事件冒泡中雨让,被點(diǎn)擊的元素div最先觸發(fā)click事件雇盖,然后,click事件沿著DOM樹(shù)一路向上栖忠,在經(jīng)過(guò)的每個(gè)節(jié)點(diǎn)上以此觸發(fā)崔挖,直至到達(dá)document對(duì)象
所有現(xiàn)代瀏覽器都支持事件冒泡,但實(shí)現(xiàn)方式存在部分變化庵寞,如IE5.5早期版本會(huì)跳過(guò)html元素狸相,直接從body到document,現(xiàn)代瀏覽器中的事件會(huì)一直冒到window
取消冒泡(阻止事件冒泡)
- 捕獲不可以取消捐川,但冒泡可以
-
e.stopPropagation()
可以中斷(取消)冒泡脓鹃,瀏覽器不在向上走 - 一般用于封裝某些獨(dú)立組件
不可阻止默認(rèn)動(dòng)作
- 阻止默認(rèn)動(dòng)作語(yǔ)法:
e.preventDefault()
- 所有冒泡皆可取消,但默認(rèn)動(dòng)作有的可以取消有的不能取消
- MDN搜索scroll event古沥,看到 Bubbles(該事件是否冒泡) 和 Cancelable(開(kāi)發(fā)者是否可以阻止默認(rèn)事件)
-
取消scroll滾動(dòng)事件:
不可取消默認(rèn)動(dòng)作瘸右,因?yàn)楝F(xiàn)有滾動(dòng)才有滾動(dòng)事件
但可以阻止wheel和touchstart的默認(rèn)動(dòng)作,注意需要找準(zhǔn)滾動(dòng)條所在元素岩齿,同時(shí)用CSS讓滾動(dòng)條width:0;
也可使用overflow:hidden;
直接取消滾動(dòng)條,但此時(shí)JS仍然可以修改scrollTop
2.3 addEventListener()
W3C模型
- 先捕獲(父 => 子)太颤,再冒泡(子 => 父)
- 特例:當(dāng)只有一個(gè)div被監(jiān)聽(tīng)(不考慮父子同時(shí)被監(jiān)聽(tīng)),fn分別在捕獲階段和冒泡階段監(jiān)聽(tīng)click事件盹沈,用戶(hù)點(diǎn)擊的元素就是開(kāi)發(fā)者監(jiān)聽(tīng)的栋齿,則誰(shuí)先監(jiān)聽(tīng)誰(shuí)先執(zhí)行(2022年已修復(fù),變?yōu)橄炔东@再冒泡)
- 注意e對(duì)象會(huì)被傳給所有監(jiān)聽(tīng)函數(shù)
- 事件結(jié)束后襟诸,e對(duì)象就不存在了(其實(shí)是存在的瓦堵,只是變成了currentTarget = null,更多了解后面再討論歌亲,暫時(shí)認(rèn)為不存在了)菇用,如果需要繼續(xù)使用,可以使用變量保存
const t = e.currentTarget
注意:關(guān)于target
和currentTarget
的區(qū)別:
- e.target—用戶(hù)操作的元素
- e.currentTarget—程序員監(jiān)聽(tīng)的元素
- this就是e.currentTarget
- 實(shí)例:div > span{文字}陷揪,用戶(hù)點(diǎn)擊文字時(shí)惋鸥,e.target就是span杂穷,e.currentTarget是div
事件綁定API
- IE5*:
// 冒泡
father.attachEvent('onclick', fn)
- 網(wǎng)景(Netscape ):
// 捕獲
father.addEventListener('click', fn)
- W3C:
// bool處為布爾值,決定采用冒泡還是捕獲卦绣,
// 不填時(shí)默認(rèn)為false耐量,則使用冒泡
// 填true時(shí)為網(wǎng)景的捕獲
father.addEventListener('click', fn,bool)
3 事件委托
事件委托就是利用冒泡的原理,但事件觸發(fā)時(shí)滤港,把要做的事件委托給父元素(或父元素的父元素)來(lái)處理廊蜒,將事件加到父級(jí)上,通過(guò)判斷事件來(lái)源的子集溅漾,執(zhí)行相應(yīng)操作山叮,從而避免對(duì)特定每個(gè)節(jié)點(diǎn)添加事件監(jiān)聽(tīng)器,減少占用內(nèi)存空間添履,提升性能屁倔。
事件委托利用事件冒泡,可以只使用一個(gè)事件處理程序來(lái)管理一種類(lèi)型的事件暮胧。
實(shí)例:
情景:需要給100個(gè)按鈕添加點(diǎn)擊事件锐借。
解決方法:監(jiān)聽(tīng)這100個(gè)按鈕的祖先,由于這100個(gè)按鈕都是祖先的后代往衷,所以他們的事件都會(huì)向上冒泡钞翔,最終都會(huì)由添加給祖先的函數(shù)來(lái)處理,只需要檢查具體子元素(event對(duì)象)的相應(yīng)屬性/內(nèi)容就可以確定炼绘,然后執(zhí)行相應(yīng)操作即可。需要監(jiān)聽(tīng)目前不存在的元素的點(diǎn)擊事件時(shí)妄田,也可以通過(guò)事件委托實(shí)現(xiàn)俺亮。
解決方法:監(jiān)聽(tīng)目標(biāo)元素的祖先,等點(diǎn)擊的時(shí)候再查看是否是需要的監(jiān)聽(tīng)的元素即可
優(yōu)點(diǎn)
- 減少頁(yè)面所需內(nèi)存疟呐,提升整體性能
- 可以用于監(jiān)聽(tīng)動(dòng)態(tài)元素
- 節(jié)省花在設(shè)置頁(yè)面事件處理程序上的時(shí)間(只用指定一個(gè)處理函數(shù)脚曾,節(jié)省DOM引用)
- document 對(duì)象隨時(shí)可用,任何時(shí)候都可以給它添加事件處理程序启具,只要頁(yè)面渲染出可點(diǎn)擊的元素本讥,就可以無(wú)延遲地起作用
適用事件
最適合使用事件委托的事件包括:click
、mousedown
鲁冯、mouseup
拷沸、keydown
和 keypress
主要事項(xiàng)
- JS支持,也不支持事件薯演,DOM事件不屬于JS的功能撞芍,屬于瀏覽器提供的DOM的功能
- JS只是調(diào)用了DOM提供的addEventListener而已