建構對話方塊元件

基礎總覽說明如何使用 <dialog> 元素建構可自動調整顏色、回應式,以及可存取的迷你及大型互動視窗。

在這篇文章中,我想分享我對於如何打造自動調整色彩的想法。 這類回應採用 <dialog> 元素,並提供簡單易用的迷你及大型互動視窗。 立即試用示範模式查看 資料來源

在淺色和深色主題中展示大型和小型對話。

如果您喜歡看影片,請參考這篇文章的 YouTube 版本:

總覽

<dialog>敬上 元素適用於網頁內情境資訊或動作。思考何時 使用者能藉由相同網頁動作 (而非多網頁) 帶來好處 動作:可能是因為表單太小,或是使用者只需要 使用者是否已確認或取消

<dialog> 元素近期已在所有瀏覽器中保持穩定:

瀏覽器支援

  • 37
  • 79
  • 98
  • 15.4

資料來源

我發現這個元素缺少一些資訊,因此這個 GUI 挑戰:新增開發人員體驗 預期的項目:其他事件、光源關閉、自訂動畫和迷你人物 以及大型類型

標記

<dialog> 元素的基本要素是簡單好用。這個元素會 自動隱藏 ,並內建各種樣式,疊加在內容上。

<dialog>
  …
</dialog>

我們可以改善這個基準。

一般來說,對話方塊元素會與互動視窗共用許多項目,且通常會使用名稱 這些元件可互換我將自由使用對話方塊元素 小型對話方塊彈出式視窗 (迷你) 和完整頁面對話方塊 (MB)。我命名了 這些對話方塊會因用途不同而有些微調整 我們新增了 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>

超級對話方塊

大型對話方塊的其中三個元素: <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 監控焦點 離開元素後,該元素便會攔截並放回。

瀏覽器支援

  • 102
  • 102
  • 112
  • 15.5

資料來源

inert之後,文件的任何部分都可以「凍結」只是因為他們感到意外 無法聚焦目標或無法與滑鼠互動而非直接交易 焦點則會引導至文件中唯一的互動部分。

開啟和自動對焦元素

根據預設,對話方塊元素會將焦點指派給第一個可聚焦的元素 。如果這不是使用者預設選擇的元素 請使用 autofocus 屬性。如先前所述 把這個項目放在取消按鈕上,而不是確認按鈕。這可以確保 確認是故意進行的,並非意外。

使用 Esc 鍵關閉

請務必輕易關閉這個可能造成乾擾的元素。 幸好,對話方塊元素會為您處理逸出鍵 來自協調作業的負擔

樣式

以下為設定對話方塊元素和硬路徑樣式的簡單路徑。簡單易用 方法是不變更對話方塊的顯示屬性 以及相關限制。我會簡單費心提供自訂動畫 開啟及關閉對話方塊,接管 display 屬性等。

使用開放式問題設定樣式

想加速自動調整色彩和整體設計一致性嗎? 導入 Open Props 這個 CSS 變數程式庫。於 除了免費的變數之外,我也匯入了 正規化檔案和 buttons (開啟提案) 可提供選用匯入功能這些匯入功能可協助我專注於自訂 即使沒有多種樣式支援 很好

設定 <dialog> 元素的樣式

擁有顯示屬性

對話方塊元素的預設顯示和隱藏行為會切換顯示畫面 屬性從 blocknone。但很抱歉,這意味著無法以動畫方式呈現 無論進出去,成分我想要同時為內外加入動畫 第一個步驟是 自行設定 display 屬性顯示特定內容:

dialog {
  display: grid;
}

變更並顯示屬性值,如 在 CSS 程式碼片段上方,需要管理大量樣式 讓使用者獲得良好的體驗首先,對話方塊的預設狀態是 已打烊。您可以使用圖表呈現這個狀態,並避免對話方塊 接收下列樣式的互動:

dialog:not([open]) {
  pointer-events: none;
  opacity: 0;
}

現在對話方塊不會顯示,而且在未開啟的情況下無法互動。稍後再說 我會加入一些 JavaScript 來管理對話方塊的 inert 屬性,確保 使用者也無法進入隱藏的對話方塊。

為對話方塊提供自動調整色彩主題

顯示淺色和深色主題的大型對話方塊,呈現表面顏色。

color-scheme 將您的文件設為瀏覽器提供的 我想自訂淺色和深色系統偏好設定的自動調整色彩主題 讓對話方塊元素超過該值開放式提案提供了幾個介面 可自動調整的顏色 淺色和深色系統偏好設定,與使用 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 單位。

大型對話方塊的位置

如要協助調整對話方塊元素的位置,建議您詳細介紹 「全螢幕背景」和對話方塊容器。背景必須 完整涵蓋所有內容,並提供著色效果,協助這個對話方塊 因為位於前方,背後的內容都無法存取對話方塊容器可以 將這個背景置中,並採用其內容所需的任何形狀。

以下樣式會將對話方塊元素固定在視窗上,並延伸至每個 並使用 margin: auto 將內容置中:

dialog {
  …
  margin: auto;
  padding: 0;
  position: fixed;
  inset: 0;
  z-index: var(--layer-important);
}
超級行動裝置對話方塊樣式

在小型可視區域上,我會稍微調整整個頁面的大型互動視窗樣式。I 將底部邊界設為 0,讓對話方塊內容顯示在底部 檢視區域只要調整一些樣式,我就能將對話方塊變成 更靠近使用者拇指的動作表:

@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敬上 在大型對話方塊中:

瀏覽器支援

  • 76
  • 79
  • 103
  • 18

資料來源

dialog[modal-mode="mega"]::backdrop {
  backdrop-filter: blur(25px);
}

我也選擇在 backdrop-filter 執行轉場效果,希望瀏覽器可以 未來將能轉換背景元素:

dialog::backdrop {
  transition: backdrop-filter .5s ease;
}

大型對話方塊的螢幕截圖,疊加顯示彩色顯示圖片的模糊背景。

設定額外項目樣式

我把這個區段命名為「額外」因為跟我的對話方塊元素有關 示範對話方塊元素的整體用途

捲動隔離設定

當對話方塊顯示時,使用者仍能捲動後方的頁面, 我不想這樣做:

一般情況下 overscroll-behavior敬上 是我常用的解決方案,但根據 spec、 這並非捲動通訊埠,因此不會影響對話方塊 這樣就不會有任何可防止的情況我可以使用 JavaScript 本指南的新事件 (例如「已關閉」和「已開啟」 文件的 overflow: hidden,或等待 :has() 穩定於 所有瀏覽器:

瀏覽器支援

  • 105
  • 105
  • 121
  • 15.4

資料來源

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>

這個元素的作用是提供對話方塊內容與優惠的標題 顯眼的關閉按鈕此外,它還會提供表面顏色 隱藏在對話方塊報導內容的後方這些要求會導致 Flexbox 容器、垂直對齊的項目,以及與邊緣的間距 邊框間距和空白處,為標題和關閉按鈕騰出空間:

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);
  }
}

在對話方塊標題上重疊顯示 Flexbox 版面配置資訊的 Chrome 開發人員工具螢幕截圖。

設定標題關閉按鈕樣式

這個示範模式使用的是「開啟問題」按鈕,因此專為使用者自訂的「關閉」按鈕 變成圓形圖示中心的按鈕,如下所示:

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;
}

Chrome 開發人員工具螢幕截圖,疊加標頭關閉按鈕的大小與邊框間距資訊。

設定對話方塊樣式 <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);
  }
}

頁尾的角色是包含動作按鈕選單。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);
  }
}

Chrome 開發人員工具螢幕截圖,重疊在頁尾元素上疊加 Flexbox 版面配置資訊。

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;
}

在頁尾選單元素上疊加 Flexbox 資訊的 Chrome 開發人員工具螢幕截圖。

動畫

對話方塊元素通常是動畫,因為這類元素會進入及離開視窗。 為這個入口和出口提供對話方塊,讓使用者能夠得到輔助的動作 在流程中調整自身方向

一般來說,對話方塊元素只能以動畫形式呈現,不能顯示。這是因為 瀏覽器會切換元素的 display 屬性。先前 將顯示方式設為格線,而且永不設為無。因此使用者可以 製作動畫

開放式 Props 內建許多主要畫面格 動畫 這樣就能輕鬆且易讀以下是動畫目標和疊加 做法:

  1. 減少動態效果是預設轉場效果,簡單的不透明度淡入和淡出。
  2. 如果動作沒有問題,請加入滑動及縮放動畫。
  3. 大型對話方塊的回應式行動裝置版面配置經過調整,以便滑出。

安全又有意義的預設轉換效果

開放式問題會提供可淡入和淡出的主要畫面格,但我偏好使用 多層轉場效果預設使用主要畫面格動畫 才能升級我們先前已為對話方塊的顯示設定樣式 不透明度,根據 [open] 屬性自動化調度管理 10。目的地: 轉換 0% 和 100% 時,可以向瀏覽器指出 想要的加/減速設定:

dialog {
  transition: opacity .5s var(--ease-3);
}

在轉場效果中加入動態效果

如果使用者覺得動作不順,大聲調和迷你對話方塊都應滑動 並在使用者離開畫面時向外擴充。做法如下: prefers-reduced-motion 媒體查詢和一些開放式問題:

@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;
  }
}

為行動裝置調整離開動畫

先前在「樣式」部分,大型對話方塊樣式已調整為適用於行動裝置 感覺像是行動單 而且仍固定在畫面底部。體重計 退出動畫並不適合這個新設計 一些媒體查詢和一些未結論:

@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」屬性,而在示範中,我會使用這些屬性修改 如果使用者提交了新圖片,則系統會顯示顯示圖片清單。

為此,請建立名為 closingclosed 的兩個新事件。接著 監聽對話方塊中的內建關閉事件。接著將對話方塊設為 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 函式,也用於建立浮動式訊息 元件,則根據 或轉場效果這就是 dialogClose 的原因 為非同步 函式; 然後 await 並放心地展開封閉式活動

新增開啟/開啟的活動

這些事件並不容易新增,因為內建的對話方塊元素沒有 就會產生一個開啟的事件 例如接近尾聲我使用 MutationObserver ,提供對話方塊屬性變動的深入分析。在這個觀察器中 我會留意開放式屬性有哪些異動,以及管理自訂事件 。

和我們開始關閉和結束事件的方式類似,請建立兩個新活動 名為 openingopened。我們先前聆聽對話對話方塊的位置 這次事件,這次使用已建立的變動觀察器,查看對話方塊的 屬性。

…
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)
      }
    })
  })
})

每當新增或移除子項時,系統都會呼叫異動觀察器回呼 文件內文觀察的突變 removedNodes 具有 nodeName/ 一個對話方塊如果對話方塊遭到移除,則點擊和關閉事件會移除 釋放記憶體後,就會分派自訂已移除的事件。

移除載入屬性

防止對話方塊動畫在加入 元素時播放其結束動畫 或網頁載入時,對話方塊中加入了載入屬性。 下列指令碼會等待對話方塊動畫執行完畢,然後移除 屬性。現在對話方塊可以隨意加入動畫 實際上卻隱藏了會造成乾擾的動畫

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 事件中,攻擊者必須 確認對話方塊是否關閉、取消或已確認如果已確認, 接著,指令碼擷取表單值並重設表單。重設非常實用 對話方塊再次顯示時,就會呈現空白,可供新提交內容。

結論

現在你知道我怎麼了,這樣會如何 🙂?

讓我們來體驗多元的方法,瞭解透過網路建立內容的所有方式。

建立示範、將 Twitter 推文連結,我們就會為您新增 前往下方的社群重混專區!

社群重混作品

資源