基礎總覽說明如何使用 <dialog>
元素建構可自動調整顏色、回應式,以及可存取的迷你及大型互動視窗。
在這篇文章中,我想分享如何使用 <dialog>
元素,建立可自適應顏色、回應式且符合無障礙設計的迷你和超級模式對話方塊。歡迎立即試用並查看原始碼!
如果你偏好觀看影片,請參閱這篇文章的 YouTube 版本:
總覽
<dialog>
元素非常適合用於頁面內的背景資訊或動作。請考慮使用者體驗何時可從同頁動作而非多頁動作中受益:可能是因為表單很小,或是使用者只需要確認或取消的動作。
<dialog>
元素近期已在所有瀏覽器中保持穩定:
我發現元素缺少了一些東西,因此在這個 GUI 挑戰中,我加入了預期的開發人員體驗項目:額外事件、輕型關閉、自訂動畫,以及迷你和超級類型。
標記
<dialog>
元素的基本元素不多,系統會自動隱藏元素,並內建樣式來疊加內容。
<dialog>
…
</dialog>
我們可以改善這個基準。
一般來說,對話方塊元素與模式會有很多相似之處,因此名稱通常可以互換使用。我在這裡使用對話方塊元素,用於小型對話方塊彈出式視窗 (迷你) 和整頁對話方塊 (超級)。我將這兩個對話方塊命名為「mega」和「mini」,並針對不同的用途稍加調整。我新增了 modal-mode
屬性,讓您指定類型:
<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>
不一定,但一般會使用對話方塊元素來收集一些互動資訊。對話方塊元素中的表單會連結在一起。建議您使用表單元素包裝對話方塊內容,讓 JavaScript 能夠存取使用者輸入的資料。此外,使用 method="dialog"
的表單內按鈕可以在不使用 JavaScript 的情況下關閉對話方塊,並傳遞資料。
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
…
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
Mega 對話方塊
超級對話方塊的單一表單中包含三個元素:<header>
、<article>
和 <footer>
。這些元素可做為語意容器,以及對話方塊呈現的樣式目標。標頭會標示彈出式視窗,並提供關閉按鈕。本文說明表單輸入內容和資訊。頁尾會保留動作按鈕的 <menu>
。
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
<header>
<h3>Dialog title</h3>
<button onclick="this.closest('dialog').close('close')"></button>
</header>
<article>...</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
第一個選單按鈕具有 autofocus
和 onclick
內嵌事件處理常式。autofocus
屬性會在對話方塊開啟時接收焦點,而最佳做法是將其放在取消按鈕,而非確認按鈕。這可確保確認動作是經過深思熟慮而非意外。
迷你對話方塊
迷你對話方塊與超級對話方塊非常相似,只是缺少 <header>
元素。這樣就能縮小圖片大小,並讓圖片更貼近文字。
<dialog id="MiniDialog" modal-mode="mini">
<form method="dialog">
<article>
<p>Are you sure you want to remove this user?</p>
</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
對話方塊元素可為完整可視區域元素提供堅實的基礎,以便收集資料和使用者互動。這些基本知識有助於在網站或應用程式中 帶來一些有趣又實用的互動體驗。
無障礙設定
對話方塊元素內建的無障礙功能非常完善。與我平常的做法不同,許多功能已經存在。
還原焦點
如同我們在建構側邊導覽元件中手動執行的操作,開啟和關閉某個項目時,務必正確將焦點放在相關的開啟和關閉按鈕上。側邊導覽列開啟時,焦點會放在關閉按鈕上。按下關閉按鈕時,焦點會還原至開啟該按鈕的按鈕。
對話方塊元素的內建預設行為如下:
很抱歉,如果您想讓對話方塊以動畫方式顯示和隱藏,就無法使用這項功能。在 JavaScript 部分中,我會恢復該功能。
擷取焦點
對話方塊元素會為您管理文件的 inert
。在 inert
之前,JavaScript 會監控焦點離開元素的情況,並在該時機攔截並放回焦點。
在 inert
之後,文件的任何部分都可以「凍結」,也就是不再是焦點目標,或無法與滑鼠互動。系統不會將焦點鎖定在某個位置,而是將焦點引導至文件中唯一的互動部分。
開啟並自動對焦元素
根據預設,對話方塊元素會將焦點指派給對話方塊標記中第一個可聚焦的元素。如果這不是使用者預設的最佳元素,請使用 autofocus
屬性。如前所述,我認為最佳做法是將這項資訊放在取消按鈕上,而非確認按鈕。這樣可確保確認動作是經過思考而非意外發生。
使用 Esc 鍵關閉
請務必讓使用者輕鬆關閉這個可能會中斷的元素。幸運的是,對話方塊元素會為您處理 Escape 鍵,讓您不必負擔協調工作。
樣式
您可以輕鬆為對話方塊元素設定樣式,也可以使用較難的路徑。您可以不變更對話方塊的顯示屬性,並配合其限制來實現簡易路徑。我會採用較困難的做法,為開啟和關閉對話方塊提供自訂動畫,接管 display
屬性等等。
使用開放式問題設定樣式
為了加快自適應色彩和整體設計的一致性,我毫不客氣地引入了 CSS 變數程式庫 Open Props。除了提供的免費變數,我還匯入了normalize 檔案和一些按鈕,這兩者都是 Open Props 提供的選用匯入項目。這些匯入作業可讓我專注於自訂對話方塊和示範,而不需要許多樣式來支援對話方塊,並讓對話方塊看起來更美觀。
為 <dialog>
元素設定樣式
擁有顯示屬性
對話方塊元素的預設顯示和隱藏行為會將顯示屬性切換為 block
或 none
。很抱歉,這表示無法進行進出動畫,只能進行進場動畫。我想為進入及離開動畫製作動畫,第一步是設定自己的 display 屬性:
dialog {
display: grid;
}
如上方 CSS 程式碼片段所示,您可以變更並擁有顯示屬性值,但為了提供良好的使用者體驗,您需要管理大量的樣式。首先,對話方塊的預設狀態是關閉的。您可以以視覺化的方式呈現此狀態,並避免對話方塊收到與下列樣式的互動:
dialog:not([open]) {
pointer-events: none;
opacity: 0;
}
對話方塊現在是隱藏的,且在未開啟時無法與其互動。稍後,我會新增一些 JavaScript 來管理對話方塊上的 inert
屬性,確保鍵盤和螢幕閱讀器使用者也無法存取隱藏的對話方塊。
為對話方塊提供自動調整色彩主題
雖然 color-scheme
會將文件設為使用瀏覽器提供的適應性色彩主題,以便使用淺色和深色系統偏好設定,但我希望對對話方塊元素進行更多自訂。Open Props 提供幾種表面顏色,可自動調整為淺色和深色系統偏好設定,類似於使用 color-scheme
。這些元素非常適合用於設計中建立的圖層,我很喜歡使用顏色來協助視覺化呈現圖層介面的外觀。背景顏色為 var(--surface-1)
;如要置於該圖層上方,請使用 var(--surface-2)
:
dialog {
…
background: var(--surface-2);
color: var(--text-1);
}
@media (prefers-color-scheme: dark) {
dialog {
border-block-start: var(--border-size-1) solid var(--surface-3);
}
}
我們日後會為子元素 (例如標頭和頁尾) 新增更多自適應色彩。我認為這些元素是對話方塊的額外元素,但在設計引人入勝且設計良好的對話方塊時,這些元素非常重要。
回應式對話方塊大小
對話方塊預設會將大小委派給內容,這通常是個不錯的做法。我的目標是將 max-inline-size
限制在可讀取的大小 (--size-content-3
= 60ch
) 或可視區域寬度的 90%。這樣一來,對話方塊在行動裝置上不會從邊到邊,在電腦螢幕上也不會太寬,以免難以閱讀。接著新增 max-block-size
,讓對話方塊不會超過頁面高度。這也表示,如果對話方塊元素很高,我們就需要指定對話方塊的捲動區域。
dialog {
…
max-inline-size: min(90vw, var(--size-content-3));
max-block-size: min(80vh, 100%);
max-block-size: min(80dvb, 100%);
overflow: hidden;
}
請注意,我有兩個 max-block-size
,第一個使用 80vh
,也就是實體檢視區單位。我真正希望的是,為國際使用者在相對流程中保留對話方塊,因此我在第二個宣告中使用邏輯且較新的 dvb
單元,以便在穩定性提升時使用。
Mega 對話方塊位置
為了協助定位對話方塊元素,建議您將其分為兩個部分:全螢幕背景和對話方塊容器。背景必須覆蓋所有內容,提供遮蔽效果,以便在對話方塊前方顯示,並遮蔽後方無法存取的內容。對話方塊容器可自由在這個背景上居中,並採用內容所需的任何形狀。
下列樣式會將對話方塊元素固定在視窗中,並將其延伸至各個角落,並使用 margin: auto
將內容置中:
dialog {
…
margin: auto;
padding: 0;
position: fixed;
inset: 0;
z-index: var(--layer-important);
}
行動裝置超大對話方塊樣式
在小型檢視區中,我會為這個全頁 mega 模態視窗設定不同的樣式。我將底部邊界設為 0
,這樣對話方塊內容就會顯示在可視區域的底部。透過幾項樣式調整,我可以將對話方塊轉換為 actionsheet,讓使用者更容易操作:
@media (max-width: 768px) {
dialog[modal-mode="mega"] {
margin-block-end: 0;
border-end-end-radius: 0;
border-end-start-radius: 0;
}
}
迷你對話方塊位置
使用較大的可視區域 (例如電腦) 時,我選擇將迷你對話方塊放在呼叫這些區域的元素上。如要執行這項操作,我需要使用 JavaScript。這裡提供了我使用的技巧,但我認為這些技巧已不在本文的討論範圍內。如果沒有 JavaScript,迷你對話方塊會顯示在畫面中央,就像超大型對話方塊一樣。
讓內容脫穎而出
最後,在對話方塊中加入一些功能,看起來就好像位在頁面上方的軟面。圓角可讓對話方塊看起來更柔和。您可以使用 Open Props 精心設計的陰影道具來達成深度效果:
dialog {
…
border-radius: var(--radius-3);
box-shadow: var(--shadow-6);
}
自訂背景假元素
我選擇以輕鬆的方式處理背景,只在對話方塊中加入 backdrop-filter
的模糊效果:
dialog[modal-mode="mega"]::backdrop {
backdrop-filter: blur(25px);
}
我還選擇在 backdrop-filter
上加入轉場效果,希望瀏覽器日後能允許轉場背景元素:
dialog::backdrop {
transition: backdrop-filter .5s ease;
}
樣式額外資訊
我將這個部分稱為「額外內容」,因為它與對話方塊元素的一般用法相比,更與對話方塊元素示範有關。
捲動容器
對話方塊顯示時,使用者仍能捲動後方的頁面,我不想這麼做:
通常,overscroll-behavior
是我通常採用的解決方案,但根據規格,它不會對對話方塊產生影響,因為它不是捲動端口,也就是說,它不是捲動器,因此沒有任何可防止的情況。我可以使用 JavaScript 監控本指南中的新事件 (例如「closed」和「opened」),並在文件中切換 overflow: hidden
,也可以等待 :has()
在所有瀏覽器中穩定運作:
html:has(dialog[open][modal-mode="mega"]) {
overflow: hidden;
}
現在當開啟大型對話方塊時,html 文件會包含 overflow: hidden
。
<form>
版面配置
除了收集來自使用者的互動資訊這項重要元素之外,我也能用來設定標頭、頁尾和文章元素的版面配置。我打算透過這個版面配置,將文章子項定義為可捲動的區域。我使用 grid-template-rows
達成這項目標。文章元素會提供 1fr
,且表單本身的最大高度與對話方塊元素相同。設定這個固定高度和固定列大小,可讓文章元素受到限制,並在溢出時捲動:
dialog > form {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
max-block-size: 80vh;
max-block-size: 80dvb;
}
為對話方塊 <header>
設定樣式
這個元素的角色是提供對話方塊內容的標題,以及方便找到的關閉按鈕。它也會提供一個介面顏色,讓它看起來像是在對話方塊文章內容的後方。這些需求會導致彈性容器容器、垂直對齊的項目與邊緣間距,以及一些邊框和間距,讓標題和關閉按鈕有足夠的空間:
dialog > form > header {
display: flex;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
background: var(--surface-2);
padding-block: var(--size-3);
padding-inline: var(--size-5);
}
@media (prefers-color-scheme: dark) {
dialog > form > header {
background: var(--surface-1);
}
}
為標題關閉按鈕設定樣式
由於這個示範使用「開啟 Props」按鈕,因此關閉按鈕已自訂為以圓形圖示為主的按鈕,如下所示:
dialog > form > header > button {
border-radius: var(--radius-round);
padding: .75ch;
aspect-ratio: 1;
flex-shrink: 0;
place-items: center;
stroke: currentColor;
stroke-width: 3px;
}
為對話方塊 <article>
設定樣式
在這個對話方塊中,article 元素扮演特殊角色:在高度或長度較長的對話方塊中,這個元素是用於捲動的空間。
為達成這項目標,父項表單元素已為自身建立一些上限,可在文章元素過高時提供限制。設定 overflow-y: auto
,讓捲軸只在需要時顯示,並透過 overscroll-behavior: contain
在其中捲動,其餘則為自訂呈現樣式:
dialog > form > article {
overflow-y: auto;
max-block-size: 100%; /* safari */
overscroll-behavior-y: contain;
display: grid;
justify-items: flex-start;
gap: var(--size-3);
box-shadow: var(--shadow-2);
z-index: var(--layer-1);
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: light) {
dialog > form > article {
background: var(--surface-1);
}
}
為對話方塊 <footer>
設定樣式
頁尾的作用是包含動作按鈕的選單。Flexbox 是用來將內容對齊頁尾內嵌軸的結尾,然後為按鈕增加一些空間。
dialog > form > footer {
background: var(--surface-2);
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: dark) {
dialog > form > footer {
background: var(--surface-1);
}
}
為對話方塊底部選單設定樣式
menu
元素用於包含對話方塊的動作按鈕。並採用包含 gap
的包裝 Flexbox 版面配置,以提供按鈕之間的空間。選單元素有邊框間距,例如 <ul>
。我也會移除該樣式,因為我不需要它。
dialog > form > footer > menu {
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
padding-inline-start: 0;
}
dialog > form > footer > menu:only-child {
margin-inline-start: auto;
}
動畫
對話方塊元素經常會進入和離開視窗,因此通常會加入動畫效果。為對話方塊提供這類進入和離開動作的支援,有助於使用者在流程中找到方向。
一般來說,對話方塊元素只能以動畫方式顯示,而不能以動畫方式關閉。這是因為瀏覽器會切換元素上的 display
屬性。先前,指南會將顯示畫面設為格狀,但不會設為「無」。這可讓您啟用進出動畫功能。
Open Props 提供許多可用的關鍵影格動畫,可讓您輕鬆編排動畫,並讓動畫更易於閱讀。以下是動畫目標和我採用的圖層方法:
- 減少動態效果是預設轉場效果,可簡單地淡入和淡出不透明度。
- 如果動作沒有問題,請加入滑動及縮放動畫。
- 調整超級對話方塊的回應式行動版面配置,以便滑出。
安全且有意義的預設轉場效果
雖然 Open Props 會提供淡入和淡出的主要影格,但我更偏好將這種分層轉場方法設為預設,並將主要影格動畫設為潛在的升級項目。先前我們已使用不透明度設定對話方塊的顯示設定,並根據 [open]
屬性協調 1
或 0
。如要進行 0% 到 100% 之間的轉場效果,請告訴瀏覽器您想要的時間長度和緩和效果:
dialog {
transition: opacity .5s var(--ease-3);
}
為轉場效果加入動態效果
如果使用者允許動態效果,則 mega 和 mini 對話方塊都應在進入時滑動向上,並在離開時縮放。您可以使用 prefers-reduced-motion
媒體查詢和一些 Open Props 來達成這項目標:
@media (prefers-reduced-motion: no-preference) {
dialog {
animation: var(--animation-scale-down) forwards;
animation-timing-function: var(--ease-squish-3);
}
dialog[open] {
animation: var(--animation-slide-in-up) forwards;
}
}
為行動裝置調整離開動畫
在前面的樣式設定部分,我們將巨型對話方塊樣式調整為行動功能表,以便在行動裝置上使用,就像一張小紙張從畫面底部滑上來,並仍附在底部。縮放退出動畫不太適合這項新設計,我們可以透過幾個媒體查詢和一些 Open 屬性進行調整:
@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
dialog[modal-mode="mega"] {
animation: var(--animation-slide-out-down) forwards;
animation-timing-function: var(--ease-squish-2);
}
}
JavaScript
使用 JavaScript 時,可加入以下幾點:
// dialog.js
export default async function (dialog) {
// add light dismiss
// add closing and closed events
// add opening and opened events
// add removed event
// removing loading attribute
}
這些新增項目源自對淺色關閉 (按一下對話方塊背景)、動畫 (以及一些其他事件),讓取得表單資料的時間更短。
新增關閉燈光
這項工作很簡單,而且是對話方塊元素不含動畫的絕佳補充。這項互動是透過監控對話方塊元素的點擊,並利用事件冒泡來評估點選的項目,且只會在該元素為最上層元素時close()
:
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
}
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
請注意 dialog.close('dismiss')
。系統會呼叫事件並提供字串。其他 JavaScript 可以擷取這個字串,藉此深入瞭解對話方塊的關閉方式。您會發現,每次從各種按鈕呼叫函式時,我也會提供關閉字串,為應用程式提供使用者互動相關的背景資訊。
新增關閉和已關閉事件
對話方塊元素會附帶關閉事件:在呼叫對話方塊 close()
函式時,系統會立即發出此事件。由於我們要為這個元素製作動畫,因此最好在動畫前後設定事件,以便在變更時擷取資料或重設對話方塊表單。我在這裡使用它來管理在關閉對話方塊時新增 inert
屬性,並在示範中使用這些屬性修改顯示圖片清單 (如果使用者已提交新圖片)。
如要做到這點,請建立兩個名為 closing
和 closed
的新事件。接著,請在對話方塊上監聽內建的關閉事件。從這裡開始,將對話方塊設為 inert
,並調度 closing
事件。接下來的工作是等待對話方塊上的動畫和轉場效果執行完畢,然後調度 closed
事件。
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
export default async function (dialog) {
…
dialog.addEventListener('close', dialogClose)
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
animationsComplete
函式也用於建立 Toast 元件,會根據動畫和轉場承諾的完成狀態傳回承諾。這就是 dialogClose
為何是非同步函式的原因;接著,它可以await
傳回的承諾,並確實轉移至已關閉的事件。
新增開啟和已開啟事件
這些事件並不容易新增,因為內建的對話方塊元素不提供開啟事件,像關閉事件一樣簡單。我使用 MutationObserver 提供對話方塊屬性變更的洞察資料。在這個觀察器中,我會監控 open 屬性的變更,並據此管理自訂事件。
與啟動關閉和關閉事件的方式類似,建立兩個新事件並命名為 opening
和 opened
。先前我們是監聽對話方塊關閉事件,這次則是使用已建立的變異觀察器來監控對話方塊的屬性。
…
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
export default async function (dialog) {
…
dialogAttrObserver.observe(dialog, {
attributes: true,
})
}
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
對話方塊屬性變更時,系統會呼叫突變觀察器回呼函式,並以陣列形式提供變更清單。逐一檢查屬性變更,找出要開啟的 attributeName
。接著,請檢查元素是否具有屬性:這會指出對話方塊是否已開啟。如果已開啟,請移除 inert
屬性,將焦點設為要求 autofocus
的元素,或對話方塊中找到的第一個 button
元素。最後,與關閉和已關閉事件類似,請立即調度開啟事件,等待動畫完成,然後調度已開啟事件。
新增已移除的事件
在單一頁面應用程式中,系統會根據路徑或其他應用程式需求和狀態新增及移除對話方塊。在移除對話方塊時,清理事件或資料可能會很有幫助。
您可以使用其他突變觀察器來達成這項目標。這次我們不會觀察對話方塊元素的屬性,而是觀察主體元素的子項,並留意對話方塊元素是否已移除。
…
const dialogRemovedEvent = new Event('removed')
export default async function (dialog) {
…
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
}
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
每當在文件內文中新增或移除子項時,系統就會呼叫異動觀察器回呼。系統會監控的特定變異是指具有對話方塊 nodeName
的 removedNodes
。如果對話方塊已移除,系統會移除點擊和關閉事件,以釋放記憶體,並調度自訂移除事件。
移除 loading 屬性
為避免對話方塊動畫在加入頁面或頁面載入時播放退出動畫,我們已將載入屬性新增至對話方塊。下列指令碼會等待對話方塊動畫完成執行,然後移除屬性。對話方塊現在可以自由進出動畫,我們也成功隱藏了會造成干擾的動畫。
export default async function (dialog) {
…
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
如要進一步瞭解如何防止網頁載入時出現關鍵影格動畫,請參閱相關文章。
全部
以下是完整的 dialog.js
,我們已逐一說明各個部分:
// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
const dialogRemovedEvent = new Event('removed')
// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
// wait for all dialog animations to complete their promises
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
// page load dialogs setup
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
dialog.addEventListener('close', dialogClose)
dialogAttrObserver.observe(dialog, {
attributes: true,
})
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
// remove loading attribute
// prevent page load @keyframes playing
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
使用 dialog.js
模組
模組匯出的函式預期會呼叫並傳遞對話方塊元素,以便新增這些新事件和功能:
import GuiDialog from './dialog.js'
const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')
GuiDialog(MegaDialog)
GuiDialog(MiniDialog)
這兩個對話方塊也已升級,加入輕鬆關閉功能、動畫載入修正項目,以及更多可用的事件。
監聽新的自訂事件
每個已升級的對話方塊元素現在都能監聽五個新事件,如下所示:
MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)
MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)
MegaDialog.addEventListener('removed', dialogRemoved)
以下是處理這些事件的兩個範例:
const dialogOpening = ({target:dialog}) => {
console.log('Dialog opening', dialog)
}
const dialogClosed = ({target:dialog}) => {
console.log('Dialog closed', dialog)
console.info('Dialog user action:', dialog.returnValue)
if (dialog.returnValue === 'confirm') {
// do stuff with the form values
const dialogFormData = new FormData(dialog.querySelector('form'))
console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))
// then reset the form
dialog.querySelector('form')?.reset()
}
}
在使用對話方塊元素建構的示範中,我使用關閉事件和表單資料,將新的顯示圖片元素新增至清單。時間點正確,因為對話方塊已完成退出動畫,然後一些指令碼會在新圖像中顯示動畫。多虧新的事件,自動化調度管理使用者體驗的過程可能會更順暢。
注意 dialog.returnValue
:此項目包含呼叫對話方塊 close()
事件時傳遞的關閉字串。在 dialogClosed
事件中,瞭解對話方塊是否已關閉、取消或確認,非常重要。如果確認成功,指令碼就會擷取表單值並重設表單。重設功能非常實用,因為當對話方塊再次顯示時,對話方塊會是空白,可供您重新提交。
結論
現在你知道我怎麼了,這樣會如何 🙂?
讓我們多方嘗試,瞭解在網路上建構應用程式的所有方式。
建立示範、張貼推文 連結,以便我們將其新增至下方的社群重混專區!
社群重混作品
- @GrimLink 搭配 3 合 1 對話方塊。
- @mikemai2awesome 使用不會變更
display
屬性的優質重混作品。 - 搭配 Svelte 和 Svelte FLIP 一起使用 @geoffrich_。
資源
- GitHub 上的原始碼
- Doodle 顯示圖片