你可能不知道的Dialog彈窗
除了有良好的語義外,隨著瀏覽器的不斷更新迭代,還出現了許多你可能不知道的特性,快速了解一下吧~
一、打開和關閉方法
首先,在不查閱任何官方文檔的情況下,先做一個選擇題目
請問:下面哪組方法(打開/關閉)是合法的?
思考 10 秒...
??
??
??
??
正確答案是 C,也就是
很多同學可能會覺得是 A 或者 B,但是官方文檔上確是 C,確實是一個令人迷惑的方法。至于原因,有解釋說,dialog是存在于頁面上的元素,所以打開用show,而關閉表示中斷彈窗內的行為,所以用close,只能說有聯系,但比較牽強,或許就是規范設計疏忽了,大家記住就好。
還有一個,如果需要彈窗默認顯示,大家先猜猜看?
再來思考 10 秒...
??
??
??
??
正確答案是 B,也就是
??是不是又和上面的show()對不上了?
除了dialog元素,details元素的打開屬性也是open
二、表單提交特性
彈窗很多時候的作用都是充當表單輸入。
比如一個彈窗中需要有一個關閉或者取消按鈕
通常情況下,我們可能需要用到前面提到的dialog.close()方法來主動關閉彈窗。
其實,還可以通過表單特性實現這樣一個效果,具體做法是:
- 在dialog中嵌套一層form
- 給form添加一個method=dialog屬性
如下
這樣就可以不用綁定額外的事件來實現關閉功能了,效果如下
Kapture 2023-01-14 at 12.52.33
其實原因就在于表單上的method=dialog屬性,如果表單在dialog元素中,提交時就會觸發關閉對話框
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/form#attr-method
默認情況下,表單里的button都會觸發提交,等同于type=submit,但如果設置了type=button或者type=reset,則不會觸發表單提交,自然也不會關閉彈窗
效果如下
另外還有一個小特性,dialog還有一個returnValue屬性,可以返回表單中提交按鈕的value值
在提交后可以打印彈窗的returnValue,如下
image-20230114132406007
不過暫時還沒有想到很有用的場景
三、模態窗口特性
彈窗除了可以通過dialog.show()打開之外,還提供了一個模態窗口,方法是
通過這個方法打開的彈窗,會自帶一個半透明的背景,并且完全水平垂直居中
這個半透明的背景并不是普通的元素,而是一個叫做::backdrop[1]的偽元素控制的,并且目前只有通過dailog.showModal()這個方法才能生成
要自定義背景也很容易
這樣就變成了半透明紅色
如果希望打開彈窗有動畫,可以自定義默認樣式,通過visibility的方式實現隱藏顯示
效果如下
其實這里我是不太建議動畫的,彈窗就是要反應快速,加了動畫反而會拖累整體。
另外,還可以通過:modal偽類來區分是普通彈窗還是模態彈窗
這樣可以更多進行自定義行為
模態彈窗還有一個非常省心的點,就是無需關注層級。它有一套非常直觀的規則,哪個后打開,哪個層級最高,比如這樣兩個彈窗
在不指定層級的情況下,肯定是后面的層級更高,但如果是通過dialog.showModal打開,就非常直觀了,后打開的彈窗肯定會覆蓋前面的,效果如下
說到這里,我又想到了一些UI組件庫里大到可怕的z-index,大概率就是通過計算不斷疊加得出的
四、焦點隔離特性
除了上面一些比較直觀的特性外,還有一些可能會忽略的,比如焦點的控制。
默認情況下,打開彈窗后會自動聚焦到彈窗內的第一個可聚焦元素上
Kapture 2023-01-14 at 14.34.43
還可以有input輸入框
打開彈窗后可以直接輸入,無需額外操作,簡直不要太方便
注意,注意,注意:經測試發現,如果添加了打開動畫,聚焦特性就會失效
當然,這些只是小兒科,一點點額外的 JS 也能解決,下面介紹一個系統級別的焦點隔離特性。什么意思呢?就是說在打開彈窗后,彈窗就成了一個獨立的載體,焦點只能在這個范圍內移動,也就是說,無論tab鍵如何切換,焦點不會跑到彈窗外面去,下面是一個對比效果
- 普通彈窗效果
- 模態彈窗效果
這個效果其實是和新出的inert屬性作用比較類似的,但是要比inert出現的更早,也可以說是通過inert屬性將這種隔離特性通用化了,讓平民老百姓也可以享受這種高級特性。有興趣的可以訪問我之前的這篇文章:快速了解 inert 屬性
五、頂層特性 top-layer
最后介紹一個即便是 JS 也無法模擬的系統級新特性,top-layer。
不知道大家有沒有遇到這樣的問題,有些彈窗由于業務需要,不得已寫在了某些容器下面,即便是fixed定位,也會有失效的時候,比如下面這個例子,父容器如果有transform相關屬性并且超出隱藏,就會出現這樣被裁剪的情形
上面這個例子來源于 xy-ui 中的 dialog 組件[2],后續優化,敬請期待~
在以前,或者說很多框架中,都會想辦法把彈窗放到最外層的 body下,這樣就不受影響了,比如下面是vue3中的處理方式
但現在,有了全新的 top-layer ,一切都好辦了,比如下面是一個通過dialog.showModal()打開的彈窗
你會發現,雖然dialog仍然在原來位置上,但真正渲染到了一個#top-layer的層級上,這個層級非常特殊,已經超越了html文檔流,可以說是獨一檔的存在,這樣,無論的dialog在什么位置,最后渲染的地方其實都在#top-layer層級上,自然也不會被父容器裁剪被隱藏了,示意如下
是不是和現代框架有些許類似呢?下面是一個彈窗里嵌套另外一個彈窗
效果如下
其實這也是后打開的彈窗永遠要高于之前彈窗的原因,#top-layer是動態創建的,只有打開的彈窗才會渲染到該層級之下。
另外,經測試發現,firefox 以及 safari 雖然在開發者工具上看不到#top-layer,但是dialog表現基本幾乎一致,應該是已經成為了標準規范,日后可以放心使用。
六、dom 其實也是在不斷發展的
dialog其實我在兩三年之前就有研究過,那時還沒有這么多新特性,也有可能是研究不精,后來就擱置了,然后最近在項目中用了dialog,無意中又發現了很多有意思的新特性,這意味著,dom 其實也是在不斷發展的,有必要把以前已經用過的再翻出來過一遍,說不定還能發現意想不到的結果,下面是要點總結
- dialog? 的打開和關閉方法很迷惑,分別是show() / close()
- dialog?中表單元素在添加method=dialog屬性之后,只要觸發表單提交就會自動關閉彈窗,無需額外 js
- 通過showModal()可以打開模態彈窗,并且后打開的彈窗永遠比先打開的彈窗層級要高,無需手動計算層級
- 打開彈窗會自動聚焦到彈窗內的第一個可聚焦元素,方便快速輸入
- 模態彈窗的焦點不會聚焦到彈窗外部,這和inert特性比較類似
- 模態彈窗其實是渲染到了html文檔流之外,一個叫#top-layer的層級上,因此不會受到原父容器的影響