- 原文地址:event.stopPropagation() in a modular system
- 原文做者:Frits van Campen
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Fengziyin1234
- 校對者:sworder shixi-li
在 Moxio,咱們經過叫 widgets 的模塊來構建網絡應用。一個 widget 裏面包含一些邏輯,它將控制一小部分 HTML。就像是 checkbox 元素或者一組其它的 widgets。一個 widget 能夠申明它須要的數據和依賴關係,而且能夠選擇傳遞資源去它的子組件。模塊化能夠很好的來管理複雜度,由於全部的資源傳輸的渠道都被很明確的定義了。模塊化也能夠容許你經過不一樣的組合方式來複用 widgets。JavaScript 想要真正的確保模塊化約定是有點小困難的,由於你老是能夠訪問全局做用域,固然,咱們也有辦法來解決這個問題。javascript
原生 JavaScript 的 API 在設計中並無考慮到模塊化;默認狀況下,你能夠訪問到全局做用域(global_ scope
)。咱們經過將全局資源封裝在根目錄並向下層傳遞的方式,來讓 wigets 得到到這些資源。咱們對一些資源進行了封裝,好比 LocalStorage,頁面的 URL 以及 viewport(爲了觀察在頁面內的座標)。咱們還封裝 DOMElements 和事件。經過這些封裝器,咱們能夠限制和調整功能,進而保證模塊化約定的完整。例如:一個 click 事件 可能知道 shift 鍵是否被按,可是你無法知道 click 事件的目標是什麼,由於該點擊事件的目標多是在另外一個 widget 內。這個看起來可能有很是大的限制性,可是直到目前,咱們尚未發現須要直接暴露目標的需求。html
對於每個特徵,咱們都找到了一種不破化模塊化約定的方法來表達它們。這也引出了我對於 event.stopPropagation()
的分析。咱們是否須要它?咱們如何可以提供它的功能?前端
思考一下這個 HTML 的例子:java
<div class="table">
<div class="body">
<div class="row open">
<div class="columns">
<div class="cell">
<span class="bullet"></span>
<input type="checkbox" />
Lorem ipsum dolor sit amet
</div>
<div class="cell"><a href="/lorem-ipsum">Lorem ipsum</a></div>
</div>
<div class="contents">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</div>
<!-- more rows -->
</div>
</div>
複製代碼
加了一點 CSS 後它變成了這樣:android
咱們有以下一些交互:ios
讓咱們一塊兒快速的過一遍事件
在 JavaScript 中是怎麼運做的。當你點擊一個元素節點(例如一個 checkbox),一個事件誕生,首先它沿着節點樹向下傳遞:table > body > row > columns > cell > input。這是捕捉(capturing)階段。而後,這個事件按照相反的順序向上傳遞,這個冒泡(bubble)階段:input > cell > columns > row > body > table.git
這意味着,對於 checkbox 的點擊會形成一個在 checkbox 和 row 上的 click 事件。咱們不但願點擊 checkbox 會打開/關閉 row,因此咱們須要查明這一點。這裏咱們也就引入了 stopPropagation。github
function on_checkbox_click(event) {
toggle_checkbox_state();
event.stopPropagation(); // prevent this event from bubbling up
}
複製代碼
若是處於冒泡(bubble)階段時,你在 checkbox 中的 click 事件的監聽器中加入了 event.stopPropagation()
,那麼這個事件將不會繼續向上冒泡傳遞,也就永遠不會到達 row 節點。也就簡單明瞭實現了咱們所期待的交互。後端
然而,使用 stopPropagation 有一個反作用。點擊 checkbox 的事件將 徹底
再也不向上傳遞。咱們的初衷是屏蔽在 row 節點上的點擊事件,但咱們也屏蔽了全部的父節點。例如說咱們有一個若是點擊在其餘地方,就會被關閉的打開着的菜單。那麼那個簡單明瞭的 click 監聽器就再也不適用,由於咱們的 click 事件可能會「消失」。咱們依舊可以使用捕捉(capturing)階段,可是又有什麼可以阻止位於父節點中的一個 widget 來屏蔽掉那個事件呢?stopPropagation
給咱們的模塊化帶來了矛盾。彷佛,在捕捉(capturing)和 冒泡(bubble)階段中,禁止在 widgets 中加入 event propagation 的纔是衆望所歸的選擇。網絡
若是咱們從封裝器中移除對於 stopPropagation 的支持,咱們還可以實現咱們的上述的交互麼?能夠的,可是將會很混亂。咱們能夠作一些簿記,經過記錄的方式知曉何時咱們應該忽略 row 節點上的 click 事件,或者咱們能夠新建一個事件的目標,又或者咱們讓你知道事件在哪發生。咱們實驗了一些解決方法,可是咱們並不太喜歡它們。
經過簿記來解決的例子:
var checkbox_was_clicked = false;
function on_checkbox_click() {
checkbox_was_clicked = true;
handle_checkbox_click();
}
function on_row_click() {
if (checkbox_was_clicked === false) {
handle_row_click();
}
checkbox_was_clicked = false;
}
複製代碼
你能夠看出,當咱們但願屏蔽更多的元素節點(例如第二行的連接),或者但願屏蔽的元素在次級的 widget 時,這個解決方法將會變得多麼的笨重。
咱們能夠作的更好。這裏有這樣一個概念。咱們尚未給它想好一個名字,但咱們考慮叫它 significant action
(重大的動做) 相似的名字。當你 click 時,你老是有一個最主要的動做:不論是打開/關閉 row 節點 仍是 checkbox,但歷來不會是兩者同時發生。從 UX 設計的角度來講這很道理的。個人第一個想法是 stopPropagation
不該該中止冒泡(bubble),而應該在事件中設定一個標誌來代表,一個重要的動做已經被執行了。這個方法的缺點是對於每個可交互的元素節點來講(checkbox,link,button 等等),你都須要爲它們添加一個事件觸發(handler)來設置這個標誌。那看起來會非常很大的工做量。咱們能夠稍微改進一點:對於交互元素節點,咱們已經知道它們有significant action
,因此若是目標是交互元素節點,那麼就自動設定 significant
標誌。當咱們把這樣的邏輯實如今咱們的事件封裝器時,row 節點如今只須要去檢查 significant
標誌,那麼咱們就能夠忽略來自第一列的 checkbox 和 第二列的連接的點擊事件了。
咱們能夠這樣實現咱們 row 的 click 事件觸發:
function on_row_click(event) {
if (event.is_handled() === false) { // this event had no significant action
toggle_row_open_state();
}
}
複製代碼
我常常被 JavaScript 和它的原生庫的設計中的前瞻性所驚豔。整體來講,它工做的很好。它那一種選擇你本身的冒險
式的 API 支持不少的工做流程,也包括咱們的。咱們的模塊化設計和封裝讓咱們能夠在原生庫上增長咱們的概念。咱們能夠填海移山。
咱們依舊容許 stopPropagation
的使用,可是咱們不鼓勵。significant - 標誌
已經在不少的 checkbox-table 中實現了,歡樂多多喲。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。