对于这个现象,我感到很迷惑,我依稀记得,在几个月前,<em>Chrome</em> 还不是这样的行为,盲猜是不是因为 <em>Chrome</em> 版本的问题呢?以上动图的 <em>Chrome</em> 版本是 90.0.4430.212 因此我找了个 <em>Chrome</em> 版本
2021-05-27 20:30:21
「來源: |程式設計微刊 ID:wangxiaoting678」
大家好,我是秋風。
嗯...我又來了,這次又是在...楠溪和的討論中產生的問題。
那事情是怎麼樣的呢?
起因
最近楠溪在看事件相關的文章,然後就跑來和我討論說以下程式碼的執行效果和網上的文章不一致,程式碼如下:
<div><button>123</button></div><script>var btn = document.querySelector('button');var div = document.querySelector('div'); btn.addEventListener('click', function () {console.log('bubble', 'btn'); }, false); btn.addEventListener('click', function () {console.log('capture', 'btn'); }, true); div.addEventListener('click', function () {console.log('bubble', 'div'); }, false); div.addEventListener('click', function () {console.log('capture', 'div'); }, true);</script>以上是一段很簡單的事件註冊的程式碼,然後我們點選 button。
先不看結果,思考一下。
然後我們來看看結果
對於絕大多數前端老鳥來說,會脫口而出地說出以下順序。
capture divbubble btncapture btnbubble div探索
但是不管是MDN,還是網上大多數的教程而言說的都是這個順序。
對於這個現象,我感到很迷惑,我依稀記得,在幾個月前,Chrome 還不是這樣的行為,盲猜是不是因為 Chrome 版本的問題呢?
以上動圖的 Chrome 版本是 90.0.4430.212
因此我找了個 Chrome 版本為 84.0.4109.0 進行測試。
果然是版本的問題,但是事情的追蹤依然很難,由於搜尋了規範以及查了谷歌上的一些資料,並沒有很好地幫助我解決這個疑惑,我不確定是因為 Chrome 引入的 bug 還是出現了什麼問題。
因此我就向 chromium 報告了這個問題。
最終在 Chrome 開發人員的幫助下,找到了這兩個討論
https://github.com/whatwg/dom/issues/685
https://github.com/whatwg/dom/issues/746
在上述 issues 中可以看到, 起因是在 https://bugs.webkit.org/show_bug.cgi?id=174288 中,有人指出,在 webkit 中當前的事件模型,會導致含有 Shadow DOM 的情況下,子元素的捕獲事件會優先於父元素的捕獲事件觸發。
而在舊模型中,一旦達到 AT_TARGET ,所有註冊的監聽器就將按照順序被觸發,而不管他們是否被標記為捕獲。由於 Shadow DOM 會創建多個 targets ,導致了事件執行順序的錯誤。
而上述問題在 Gecko (Mozilla Firefox 的排版引擎)卻運行正常(先捕獲再冒泡)。為此 whatwg 提出了一個新的模型結構來解決這個問題。
結論
所有的疑問到此都迎刃而解了,到現在為主我們梳理一下我們的問題。
而這個版本分界線是在 Chrome 89.0.4363.0 和 89.0.4358.0。
而 Chrome 89.0.4363.0 是在 2020-12-22 釋出的,也就是最近幾個月的事情,因此近幾個月如果你的Chrome 更新了就會遇到和我一樣的現象。
在 Chrome 89.0.4363.0 以及之後版本中,目標元素的觸發事件順序不再按照註冊順序觸發!而是按照先捕獲再冒泡的形式依次執行!
然後我們再來看看這樣修改會給我們帶來怎麼樣的影響。
首先我們要明確是的,網上以前的大部分文章已經不適用於當下的 Chrome 新版本了!如果我們業務中有依賴相關的事件觸發順序,請仔細檢查!舉個
<div><button>123</button></div><script>var a = [];var btn = document.querySelector('button');var div = document.querySelector('div'); btn.addEventListener('click', function () {console.log('bubble', 'btn'); a.push(1); }, false); btn.addEventListener('click', function () {console.log('capture', 'btn'); a.push(2); }, true); div.addEventListener('click', function () {console.log('bubble', 'div'); }, false); div.addEventListener('click', function () {console.log('capture', 'div'); }, true);</script>在新版本中,當 button 元素被點選後,最終 a 的結果為 [2,1],而在舊版本中,這個結果則為 [1,2]。
那對於現階段一些線上程式碼改如何改造呢?
改進方案
那麼現在我們無法控制使用者使用哪個版本的瀏覽器,該如何解決這個問題而來避免遇到線上問題呢?
其實很簡單。
我們只需要將所有目標元素程式碼的順序都按照先書寫捕獲事件程式碼,再書寫冒泡事件程式碼,就可以相容本次的更新。
思考
所有的事情都不是一成不變的,不管是對於一些相對官方的文章或者教程我們都要抱以懷疑的態度,相信我們所看到的。也許我這篇的言論在多年之後也會是一個錯誤示例,但是是對當下問題的一個記錄。本文也還有很多不足之處,如果有問題請在評論中指出。
參考資料
https://chromium.cypress.io/
https://github.com/whatwg/dom/issues/685
https://bugs.webkit.org/show_bug.cgi?id=174288
https://github.com/whatwg/dom/issues/746
https://dom.spec.whatwg.org/#dispatching-events
相關文章
对于这个现象,我感到很迷惑,我依稀记得,在几个月前,<em>Chrome</em> 还不是这样的行为,盲猜是不是因为 <em>Chrome</em> 版本的问题呢?以上动图的 <em>Chrome</em> 版本是 90.0.4430.212 因此我找了个 <em>Chrome</em> 版本
2021-05-27 20:30:21
<em>intel</em>的11代酷睿处理器不知不觉上市快百天了,再加上618将至,高考也已迫在眉睫了,辛辛苦苦12载,犒劳一下真应该。为了让广大学子能够很好地选择一台适合自己的电竞主机,且将每一分钱都用在刀刃上,我们本次专
2021-05-27 20:01:22
真我GT暴降400元钜惠让利价让广大粉丝大呼过瘾,这通操作也使realme真我GT彻底击穿行业骁龙888终端底价,成为市售最便宜的骁龙888旗舰。作为安卓12首批搭载机型,realme真我GT在第一时间拥有了尝鲜最新<em>Android</em> 12
2021-05-27 20:01:09
据媒体报道,5月11日,来自中国的SHEIN正式超越亚马逊,成为美国安卓系统上下载次数最多的购物应用。短短6天后,SHEIN再次登顶iOS。截至目前,SHEIN已在全球54个国家iOS购物应用中排名第一,在13个国家<em>Android</em>购物应用
2021-05-27 20:01:06
配备了SYNC 4A多媒体娱乐系统,提供自然语音控制、Apple CarPlay、<em>Android</em> Auto、集成式Amazon Alexa 和SYNC AppLink应用,被称为“史上最聪明的F-150”。新车标配Co-Pilot360 2.0主动安全系统,提供防碰撞预警、
2021-05-27 20:01:01
追寻来自爱,这就是魅族品牌理念。同样由于这一点,今年我们可以看到不同的<em>Android</em>旗舰手机。尽管如此,令人惊讶的是,魅族18系列仍然有两个型号,说是两个,但实际上也只不过是针对不同的个性需求做了不同的版本,他们都
2021-05-27 20:00:55