【前端盲點】DOM事件流論證CSS盒模型是否具有厚度

範大腳腳發表於2017-12-07
前言
很久沒有扯淡了,我們今天來扯淡吧。
我今天思考了一個問題,我們頁面的dom樹到底是如何渲染的,而CSS盒模型與javascript是否有聯絡,於是便想到一個問題:
CSS的盒模型具有厚度麼???
該文只是一種幫助理解的說法,與官方定義不一定統一,權當扯淡
盒模型
稍微入門點的前端都知道CSS盒模型,於是我們不厭其煩的偷圖來用:
這個就是我們傳說中的盒模型,我們這裡先把盒模型放一放,來看我們的DOM事件流
DOM事件流
在上一篇部落格中,我們詳細說了下javascript的事件機制:【移動端相容問題研究】javascript事件機制詳解(涉及移動相容)
今天也不知道怎麼發神經了,突然對其中兩個事件引數產生興趣:
currentTarget
某事件處理程式當前正在處理的那個元素
target
事件目標(繫結事件那個dom),源事件觸發點
 
這兩個兄弟非常有意思,為了說明他的有意思,我寫了一段程式碼:
 1 <html xmlns=”http://www.w3.org/1999/xhtml”>
 2 <head>
 3     <title></title>
 4     <style type=”text/css”>
 5          #p { width: 300px; height: 300px; padding: 10px;  border: 1px solid black; }
 6          #c { width: 100px; height: 100px; border: 1px solid red; }
 7     </style>
 8 </head>
 9 <body>
10     <div id=”p”>
11         parent
12         <div id=”c”>
13             child
14         </div>
15     </div>
16     <script type=”text/javascript”>
17         var p = document.getElementById(`p`),
18         c = document.getElementById(`c`);
19         c.addEventListener(`click`, function () {
20             alert(`子節點捕獲`)
21         }, true);
22 
23         c.addEventListener(`click`, function () {
24             alert(`子節點冒泡`)
25         }, false);
26 
27         p.addEventListener(`click`, function () {
28             alert(`父節點捕獲`)
29         }, true);
30 
31         p.addEventListener(`click`, function () {
32             alert(`父節點冒泡`)
33         }, false);
34     </script>
35 </body>
36 </html>
http://sandbox.runjs.cn/show/ij4rih6x
這個傢伙其實是非常有意思的,按照我們當時的邏輯:
① 點選parent,事件首先在document上然後parent捕獲到事件,處於目標階段然後event.target也等於parent,所以觸發捕獲事件
由於target與currentTarget相等了,所以認為到底了,開始冒泡,執行冒泡事件
② 點選child情況有所不同,事件由document傳向parent執行事件,然後傳向child最後開始冒泡
上面的解釋沒問題,我們很多朋友也都清楚,但是我今天突然問題就來了,CSS盒模型尼瑪到底是怎麼排布的???
猜想一:積木式=盒模型
首先,也是最簡單的猜想,盒模型就是堆積木(其實根據上圖來看,這個想法自然而然)
這個想法簡單,而且直觀,也和我們一般的思維類似,但是問題馬上來了:
事件捕獲會先於事件冒泡發生
事件捕獲:由最先接收到事件的元素然後傳向最裡邊
雖然是中文翻譯,我們可以得到兩個重要資訊(點選child div、忽略document):
① parent div 應該先於child div觸發
② “最裡面”,這個最裡面應該如何理解需要關注!!!
悖謬
按照上圖的理解,我們這裡就該child div事件先觸發呢,情況卻是parent div先觸發,這與常理不合,所以CSS盒模型不應該是這個樣子
於是,我這裡推翻了猜想一,這裡進行猜想二。
猜想二:盒子=盒模型
這個猜想也比較直接,因為盒模型嘛。。。。。。
最初,我在想盒模型是這個樣子,他能很好的解釋為什麼會先觸發parent的click事件而不是child的,但是他仍有一個讓我難受的地方:
我body或者parent div如果有顏色,我裡面的元素豈不是看不到了???這與現實不合啊???
於是我再一次陷入僵局,於是進入猜想三,也是最終猜想
最終幻想:盒模型=具有厚度一面透明盒子
其實用盒子來形容盒模型是非常貼切的,而且
CSS盒模型是具有厚度的
我們這裡有一個很好的形容詞來形容CSS盒模型:捅不破的XX膜!
PS:這裡本來想畫一個圖的但是,確實畫不出來,各位將就點聽我描述吧,我覺得
這個應該是我們的盒模型了,由此我們就可以更好的說明我們的答案了:
① 點選parent區域時,我們首先觸發一次點選,流程如下:
document=》body=》parent div=開始冒泡=》parent div=》body=》document
我們因為在parent div上註冊了事件,所以他會執行
② 點選child區域時
document=》body=》parent div=》child div=開始冒泡=》child div =》parent div=》body=》document
這裡情況也比較明顯了,我們一次點選滑鼠會點破透明膜,然後一次會碰到上面的元素(離開時候會記住碰到的最深dom也就是target)
底層事件模擬(純粹幻想)
好了,我們這裡來做一次詳細的事件模擬:
① 首先我們組織我們的dom樹,為dom繫結事件(這裡繫結事件我就當向hash中傳入了值)
 1 el.addEventListener(type, fn, useCapture);
 2 //每個dom在瀏覽器中肯定會具有一個唯一標識,我不知道是什麼,暫且認為是uuid
 3 //於是上面的程式碼在瀏覽器中大概是這個意思
 4 
 5 var hashCapture = {};//儲存捕獲事件集合
 6 var hashBubble = {};//儲存冒泡事件集合
 7 
 8 if (useCapture) {
 9     //裝載捕獲時的click事件
10     pushSet(el, type, fn, hashCapture);
11 } else {
12     //裝載冒泡時的click事件
13     pushSet(el, type, fn, hashCapture);
14 }
15 
16 function pushSet(el, type, fn, hashSet) {
17     hashSet[el.uuid] = {};
18     hashSet[el.uuid][type].push(fn);
19 }
PS:注意,此處完全是一種假想,我並不知道系統底層如何實現
按照上述思想,其實儲存了兩個物件:
一個關於捕獲時期dom(el)click事件的集合()——hashCapture
一個關於冒泡時期dom(el)click事件的集合()——hashBubble
② 我們開始點選,然後記錄點選過程中,手指碰到的元素(我們這裡簡化去掉document)
body=》parent div=》child div=》child div =》parent div=》body
 
按照上面的邏輯,我首先碰到了body,離開前碰到了child div,我們最好觸碰的dom就是target,所以
1 var Event = {};
2 //捕獲時碰到的dom
3 Event.captureDoms = [];
4 //冒泡時候碰到的dom
5 Event.captureDoms = [];
6 Event.target = dom;//最後碰到的元素
所以上面的target永遠都是child div,而currentTarget卻會不斷變化
captureDoms裝的是body,parent div, child div
而captureDoms 與他一致,只不過順序是反的,最好就只剩下執行事件了
PS:當我們滑鼠離開window時,判斷為一次點選,於是:
1 for (var i = 0, len < Event.captureDoms.length; i < len; i++) {
2     var el = Event.captureDoms[i];
3     var uuid = el.uuid;
4     //獲取捕獲中儲存的click事件集合
5     for (var j = 0, len1 < hashCapture[uuid].click.length; j < len1; j++) {
6         hashCapture[uuid][`click`][j](Event);//執行儲存的click事件
7     }
8 }
然後執行冒泡時候的相同邏輯即可,於是就我們的猜想結束……
firefox模型
firefox dom結構如此,但是與我的猜想好像不一樣……
結語
我們的盒模型具有厚度嗎?我認為是具有的,而且他很好的解釋了我遇到的問題,所以我認為他是具有的,我能自圓其說……
您可以考慮給小釵發個小額微信紅包以資鼓勵 
本文轉自葉小釵部落格園部落格,原文連結:http://www.cnblogs.com/yexiaochai/p/3468489.html如需轉載請自行聯絡原作者


相關文章