前言
????之所以突然想寫這個(gè)文章,主要是之前看到一篇有意思的博文麸澜,《探究點(diǎn)擊事件在JavaScript事件循環(huán)中的表現(xiàn)》秽褒,有趣的地方在于JS點(diǎn)擊事件加入回調(diào)的并不是點(diǎn)擊事件的回調(diào)方法趴酣,而是點(diǎn)擊事件本身(點(diǎn)擊位置等描述點(diǎn)擊的)
欢峰。
點(diǎn)擊不是加入回調(diào)而不是加入事件
<body style="height: 2000px">
<button class="a">打開控制臺(tái)葬荷,快速點(diǎn)擊三次</button>
<br />
<button class="b" style="height: 1000px;width: 1000px">按鈕b</button>
<script>
var btn = document.querySelector(".a");
btn.onclick = function() {
console.log("click a");
setTimeout(function() {
//頁面失去響應(yīng)2s。
var time = new Date().getTime();
while (new Date().getTime() - time < 2000) {}
window.scrollTo(100, 500); //滾動(dòng)到第二個(gè)按鈕上纽帖。
console.log("timeout");
});
};
var btn1 = document.querySelector(".b");
btn1.onclick = function() {
console.log("click b");
};
</script>
</body>
當(dāng)我們連續(xù)點(diǎn)擊button.a
兩次的時(shí)候宠漩,結(jié)果卻是
click a
timeout
click b
明明是點(diǎn)擊的button.a
,為什么會(huì)觸發(fā)button.b
的事件抛计?
????可見哄孤,點(diǎn)擊并沒有直接把回調(diào)推入事件循環(huán)(
對(duì)事件循環(huán)不了解,可以先去了解一下
)的吹截,而是推入了點(diǎn)擊事件(推測(cè)就是點(diǎn)擊位置瘦陈、事件類型...
),當(dāng)我們觸發(fā)點(diǎn)擊的時(shí)候波俄,js會(huì)去點(diǎn)擊位置尋找相應(yīng)的事件晨逝,在上面的例子中,我們?cè)邳c(diǎn)擊button.a
之后執(zhí)行了一個(gè)setTimeout
滾動(dòng)頁面到button.b
的位置懦铺,之后執(zhí)行第二次點(diǎn)擊的事件捉貌,在該位置button.a
已經(jīng)不見了,而是button.b
冬念,所以會(huì)執(zhí)行button.b
的回調(diào)事件趁窃。
事件機(jī)制
????無論通過onclick
還是addEventListener
實(shí)現(xiàn)事件綁定,我懷疑綁定機(jī)制是一樣的急前,因?yàn)樵趯?shí)際測(cè)試中醒陆,我發(fā)現(xiàn)onclick
執(zhí)行順序和addEventListener
是一樣的,也就是什么時(shí)候綁定裆针,那么就在第幾個(gè)執(zhí)行刨摩。
????addEventListener
和onclick
不同,不是直接給dom
綁定屬性世吨,并且我在dom
節(jié)點(diǎn)上也沒有看到任何相應(yīng)的對(duì)象用于保存事件澡刹,可見addEventListener
是不同的機(jī)制,參考EventEmitter
耘婚,試著去實(shí)現(xiàn)一個(gè)addEventListener
// es6的Map可以使用對(duì)象作為鍵值對(duì)的鍵值
var listeners = new Map(); // 保存dom和其對(duì)應(yīng)的事件
function addEventListener(dom, type, callback) {
if(listeners.has(dom)) {
listeners.get(dom)[type].push(callback);
} else {
listeners.set(dom, {
[type]: [callback]
})
}
}
// 點(diǎn)擊之后做了什么
// 1. 保存事件(事件類型: 'click', 觸發(fā)位置)
...
// 2. 事件循環(huán)到觸發(fā)的時(shí)候罢浇,根據(jù)點(diǎn)擊
總結(jié)
通過上面分析,總結(jié)一下
點(diǎn)擊一個(gè)
dom
之后發(fā)生了什么沐祷?
- 保存事件
向事件循環(huán)隊(duì)列中添加事件對(duì)象嚷闭,主要是(事件類型: 'click', 觸發(fā)位置: {x:,y:}戈轿,...
)- 事件循環(huán)到觸發(fā)
根據(jù)點(diǎn)擊事件對(duì)象去dom
樹中找到該dom
(不在乎是不是點(diǎn)擊的那個(gè)凌受,只是當(dāng)前該位置的dom
)- 執(zhí)行
通過listeners
找到該dom
對(duì)應(yīng)的回調(diào),并且執(zhí)行