<dialog>: 对话框元素

Baseline 广泛可用 *

此特性已经十分成熟,可在许多设备和浏览器版本上使用。自 2022 年 3 月起,它已在各浏览器中可用。

* 此特性的某些部分可能存在不同级别的支持。

<dialog> HTML 元素表示模态或非模态对话框,或其他交互式组件,例如可关闭的提示、检查器或子窗口。

HTML <dialog> 元素用于创建模态和非模态对话框。模态对话框会中断与页面其余部分的交互,使其处于非活动状态,而非模态对话框则允许与页面其余部分进行交互。

应该使用 JavaScript 来显示 <dialog> 元素。使用 .showModal() 方法显示模态对话框,使用 .show() 方法显示非模态对话框。对话框可以通过 .close() 方法关闭,或者当提交嵌套在 <dialog> 元素内的 <form> 时,使用 dialog 方法关闭。模态对话框也可以通过按下 Esc 键关闭。

属性

此元素包含全局属性

警告: tabindex 属性不得用于 <dialog> 元素。请参阅使用注意事项

closedby

指定可用于关闭 <dialog> 元素的用户操作类型。此属性区分对话框可能关闭的三种方法:

  • 一种是轻量级关闭用户操作,在这种情况下,当用户点击或点击对话框外部时,<dialog> 会关闭。这相当于“自动”状态弹出框的“轻量级关闭”行为
  • 一种是平台特定的用户操作,例如在桌面平台上按下 Esc 键,或在移动平台上进行“返回”或“关闭”手势。
  • 一种是开发者指定的机制,例如一个带有 click 处理程序的 <button>,该处理程序调用 HTMLDialogElement.close(),或者一个 <form> 提交。

可能的值是

任意

对话框可以通过以上三种方法中的任何一种关闭。

closerequest

对话框可以通过平台特定的用户操作或开发者指定的机制关闭。

none

对话框只能通过开发者指定的机制关闭。

如果 <dialog> 元素没有指定有效的 closedby 值,那么:

  • 如果它是使用 showModal() 打开的,则其行为就像值为 "closerequest" 一样。
  • 否则,其行为就像值为 "none" 一样。
open

表示对话框处于活动状态并可用于交互。如果未设置 open 属性,则对话框将对用户不可见。建议使用 .show().showModal() 方法渲染对话框,而不是 open 属性。如果 <dialog> 是使用 open 属性打开的,则它是非模态的。

注意: 虽然可以通过切换 open 属性的存在来在非模态对话框的打开和关闭状态之间切换,但此方法不推荐。有关更多信息,请参阅 open

用法说明

  • HTML <form> 元素可以用于关闭对话框,如果它们具有 method="dialog" 属性,或者用于提交表单的按钮设置了 formmethod="dialog"。当 <dialog> 内的 <form> 通过 dialog 方法提交时,对话框会关闭,表单控件的状态会被保存但不会提交,并且 returnValue 属性会被设置为被激活按钮的值。
  • CSS ::backdrop 伪元素可用于样式化模态对话框的背景,当使用 HTMLDialogElement.showModal() 方法显示对话框时,该背景会显示在 <dialog> 元素后面。例如,此伪元素可用于模糊、变暗或以其他方式遮蔽模态对话框后面的非活动内容。
  • autofocus 属性应该添加到用户预计在打开模态对话框后立即进行交互的元素上。如果没有其他元素需要更即时的交互,建议将 autofocus 添加到对话框内的关闭按钮,或者如果用户预计点击/激活对话框以将其关闭,则添加到对话框本身。
  • 请勿将 tabindex 属性添加到 <dialog> 元素,因为它不是交互式的,也不会接收焦点。对话框的内容,包括对话框中包含的关闭按钮,可以接收焦点并进行交互。

无障碍

在实现对话框时,重要的是要考虑设置用户焦点的最合适位置。当使用 HTMLDialogElement.showModal() 打开 <dialog> 时,焦点会设置到第一个嵌套的可聚焦元素上。通过使用 autofocus 属性明确指示初始焦点位置将有助于确保初始焦点设置在被认为对于任何特定对话框来说是最佳初始焦点位置的元素上。如果不确定,因为可能不总是知道对话框内的初始焦点可以设置在哪里,特别是对于对话框内容在调用时动态渲染的情况,<dialog> 元素本身可能提供最佳的初始焦点位置。

确保提供一种机制,允许用户关闭对话框。确保所有用户都能关闭对话框的最可靠方法是包含一个明确的按钮来执行此操作,例如确认、取消或关闭按钮。

默认情况下,通过 showModal() 方法调用的对话框可以通过按下 Esc 键关闭。非模态对话框默认情况下不会通过 Esc 键关闭,并且根据非模态对话框所代表的内容,可能不希望有此行为。键盘用户期望 Esc 键关闭模态对话框;确保此行为已实现并保持。如果打开了多个模态对话框,按下 Esc 键应该只关闭最后显示的对话框。使用 <dialog> 时,此行为由浏览器提供。

虽然可以使用其他元素创建对话框,但原生的 <dialog> 元素提供了可用性和辅助功能,如果您使用其他元素实现类似目的,则必须复制这些功能。如果您正在创建自定义对话框实现,请确保支持所有预期的默认行为并遵循正确的标签建议。

浏览器以类似于使用 ARIA role="dialog" 属性的自定义对话框的方式公开 <dialog> 元素。通过 showModal() 方法调用的 <dialog> 元素隐式具有 aria-modal="true",而通过 show() 方法调用或使用 open 属性显示或通过更改 <dialog> 的默认 display 来显示的 <dialog> 元素则公开为 [aria-modal="false"]。在实现模态对话框时,除了 <dialog> 及其内容之外的所有内容都应使用 inert 属性呈现为非活动状态。当将 <dialog>HTMLDialogElement.showModal() 方法一起使用时,此行为由浏览器提供。

示例

纯 HTML 对话框

此示例演示了仅使用 HTML 创建非模态对话框。由于 <dialog> 元素中的布尔值 open 属性,对话框在页面加载时显示为打开状态。通过单击“OK”按钮可以关闭对话框,因为 <form> 元素中的 method 属性设置为 "dialog"。在这种情况下,无需 JavaScript 来关闭表单。

html
<dialog open>
  <p>Greetings, one and all!</p>
  <form method="dialog">
    <button>OK</button>
  </form>
</dialog>

结果

注意: 重新加载页面以重置输出。

此对话框由于存在 open 属性而最初处于打开状态。使用 open 属性显示的对话框是非模态的。点击“OK”后,对话框会被关闭,Result 帧为空。当对话框被关闭时,没有提供重新打开它的方法。因此,显示非模态对话框的首选方法是使用 HTMLDialogElement.show() 方法。可以通过添加或删除布尔值 open 属性来切换对话框的显示,但这不推荐。

创建模态对话框

此示例演示了一个带有渐变背景的模态对话框。当“显示对话框”按钮被激活时,.showModal() 方法会打开模态对话框。对话框可以通过按下 Esc 键关闭,或者当对话框内的“关闭”按钮被激活时,通过 close() 方法关闭。

当对话框打开时,浏览器默认会将焦点放在对话框内第一个可聚焦的元素上。在此示例中,autofocus 属性应用于“关闭”按钮,使其在对话框打开时获得焦点,因为这是我们期望用户在对话框打开后立即进行交互的元素。

HTML

html
<dialog>
  <button autofocus>Close</button>
  <p>This modal dialog has a groovy backdrop!</p>
</dialog>
<button>Show the dialog</button>

CSS

我们可以使用 ::backdrop 伪元素来样式化对话框的背景。

css
::backdrop {
  background-image: linear-gradient(
    45deg,
    magenta,
    rebeccapurple,
    dodgerblue,
    green
  );
  opacity: 0.75;
}

JavaScript

对话框使用 .showModal() 方法以模态方式打开,并使用 .close().requestClose() 方法关闭。

js
const dialog = document.querySelector("dialog");
const showButton = document.querySelector("dialog + button");
const closeButton = document.querySelector("dialog button");

// "Show the dialog" button opens the dialog modally
showButton.addEventListener("click", () => {
  dialog.showModal();
});

// "Close" button closes the dialog
closeButton.addEventListener("click", () => {
  dialog.close();
});

结果

当模态对话框显示时,它会出现在可能存在的任何其他对话框之上。模态对话框之外的所有内容都是非活动状态,并且对话框之外的交互被阻止。请注意,当对话框打开时,除了对话框本身,无法与文档进行交互;“显示对话框”按钮大部分被对话框几乎不透明的背景遮蔽,并且是惰性的。

处理对话框的返回值

此示例演示了 <dialog> 元素的 returnValue 以及如何使用表单关闭模态对话框。默认情况下,returnValue 是空字符串,或者是 <dialog> 元素内提交表单的按钮的值(如果存在)。

此示例在激活“显示对话框”按钮时打开一个模态对话框。对话框包含一个带有 <select> 和两个 <button> 元素的表单,它们默认为 type="submit"。当选择选项更改时,事件监听器会更新“确认”按钮的值。如果激活“确认”按钮关闭对话框,则按钮的当前值即为返回值。如果通过按“取消”按钮关闭对话框,则 returnValuecancel

当对话框关闭时,返回值会显示在“显示对话框”按钮下方。如果对话框通过按下 Esc 键关闭,returnValue 不会被更新,并且 close 事件不会发生,因此 <output> 中的文本不会更新。

HTML

html
<!-- A modal dialog containing a form -->
<dialog id="favDialog">
  <form>
    <p>
      <label>
        Favorite animal:
        <select>
          <option value="default">Choose…</option>
          <option>Brine shrimp</option>
          <option>Red panda</option>
          <option>Spider monkey</option>
        </select>
      </label>
    </p>
    <div>
      <button value="cancel" formmethod="dialog">Cancel</button>
      <button id="confirmBtn" value="default">Confirm</button>
    </div>
  </form>
</dialog>
<p>
  <button id="showDialog">Show the dialog</button>
</p>
<output></output>

JavaScript

js
const showButton = document.getElementById("showDialog");
const favDialog = document.getElementById("favDialog");
const outputBox = document.querySelector("output");
const selectEl = favDialog.querySelector("select");
const confirmBtn = favDialog.querySelector("#confirmBtn");

// "Show the dialog" button opens the <dialog> modally
showButton.addEventListener("click", () => {
  favDialog.showModal();
});

// "Cancel" button closes the dialog without submitting because of [formmethod="dialog"], triggering a close event.
favDialog.addEventListener("close", (e) => {
  outputBox.value =
    favDialog.returnValue === "default"
      ? "No return value."
      : `ReturnValue: ${favDialog.returnValue}.`; // Have to check for "default" rather than empty string
});

// Prevent the "confirm" button from the default behavior of submitting the form, and close the dialog with the `close()` method, which triggers the "close" event.
confirmBtn.addEventListener("click", (event) => {
  event.preventDefault(); // We don't want to submit this fake form
  favDialog.close(selectEl.value); // Have to send the select box value here.
});

结果

以上示例演示了关闭模态对话框的以下三种方法:

“取消”按钮包含 formmethod="dialog" 属性,该属性会覆盖 <form> 的默认 GET 方法。当表单的方法为 dialog 时,表单的状态会保存但不会提交,并且对话框会被关闭。

如果没有 action,通过默认的 GET 方法提交表单会导致页面重新加载。我们使用 JavaScript 通过 event.preventDefault()HTMLDialogElement.close() 方法分别阻止提交并关闭对话框。

在每个 dialog 元素中提供关闭机制至关重要。Esc 键默认情况下不会关闭非模态对话框,也不能假设用户能够访问物理键盘(例如,使用触摸屏设备而无法访问键盘的用户)。

关闭带有必填表单输入的对话框

当对话框内的表单包含必填输入时,用户代理只会在您为必填输入提供值后才允许您关闭对话框。要关闭此类对话框,可以使用关闭按钮上的 formnovalidate 属性,或者在单击关闭按钮时调用对话框对象的 close() 方法。

html
<dialog id="dialog">
  <form method="dialog">
    <p>
      <label>
        Favorite animal:
        <input type="text" required />
      </label>
    </p>
    <div>
      <input type="submit" id="normal-close" value="Normal close" />
      <input
        type="submit"
        id="novalidate-close"
        value="Novalidate close"
        formnovalidate />
      <input type="submit" id="js-close" value="JS close" />
    </div>
  </form>
</dialog>
<p>
  <button id="show-dialog">Show the dialog</button>
</p>
<output></output>

JavaScript

js
const showBtn = document.getElementById("show-dialog");
const dialog = document.getElementById("dialog");
const jsCloseBtn = dialog.querySelector("#js-close");

showBtn.addEventListener("click", () => {
  dialog.showModal();
});

jsCloseBtn.addEventListener("click", (e) => {
  e.preventDefault();
  dialog.close();
});

结果

从输出中,我们看到无法使用正常关闭按钮关闭对话框。但是,如果我们在取消按钮上使用 formnovalidate 属性绕过表单验证,对话框就可以关闭。通过编程方式,dialog.close() 也会关闭此类对话框。

不同 closedby 行为的比较

此示例演示了 closedby 属性不同值之间的行为差异。

HTML

我们提供三个 <button> 元素和三个 <dialog> 元素。每个按钮都将编程为打开一个不同的对话框,演示 closedby 属性的三个值之一的行为:nonecloserequestany。请注意,每个 <dialog> 元素都包含一个将用于关闭它的 <button> 元素。

html
<p>Choose a <code>&lt;dialog&gt;</code> type to show:</p>
<div id="controls">
  <button id="none-btn"><code>closedby="none"</code></button>
  <button id="closerequest-btn">
    <code>closedby="closerequest"</code>
  </button>
  <button id="any-btn"><code>closedby="any"</code></button>
</div>

<dialog closedby="none">
  <h2><code>closedby="none"</code></h2>
  <p>
    Only closable using a specific provided mechanism, which in this case is
    pressing the "Close" button below.
  </p>
  <button class="close">Close</button>
</dialog>

<dialog closedby="closerequest">
  <h2><code>closedby="closerequest"</code></h2>
  <p>Closable using the "Close" button or the Esc key.</p>
  <button class="close">Close</button>
</dialog>

<dialog closedby="any">
  <h2><code>closedby="any"</code></h2>
  <p>
    Closable using the "Close" button, the Esc key, or by clicking outside the
    dialog. "Light dismiss" behavior.
  </p>
  <button class="close">Close</button>
</dialog>

JavaScript

这里我们为主要控制 <button> 元素、<dialog> 元素以及对话框内部的“关闭” <button> 元素分配了不同的变量引用。首先,我们使用 addEventListener 为每个控制按钮分配一个 click 事件监听器,其事件处理函数通过 showModal() 打开相关的 <dialog> 元素。然后,我们遍历“关闭” <button> 引用,为每个引用分配一个 click 事件处理函数,该函数通过 close() 关闭其 <dialog> 元素。

js
const noneBtn = document.getElementById("none-btn");
const closerequestBtn = document.getElementById("closerequest-btn");
const anyBtn = document.getElementById("any-btn");

const noneDialog = document.querySelector("[closedby='none']");
const closerequestDialog = document.querySelector("[closedby='closerequest']");
const anyDialog = document.querySelector("[closedby='any']");

const closeBtns = document.querySelectorAll(".close");

noneBtn.addEventListener("click", () => {
  noneDialog.showModal();
});

closerequestBtn.addEventListener("click", () => {
  closerequestDialog.showModal();
});

anyBtn.addEventListener("click", () => {
  anyDialog.showModal();
});

closeBtns.forEach((btn) => {
  btn.addEventListener("click", () => {
    btn.parentElement.close();
  });
});

结果

渲染结果如下:

尝试点击每个按钮打开一个对话框。第一个只能通过点击其“关闭”按钮关闭。第二个也可以通过设备特定的用户操作关闭,例如按下 Esc 键。第三个具有完整的“轻量级关闭”行为,因此它也可以通过点击或轻触对话框外部关闭。

动画对话框

<dialog> 在隐藏时设置为 display: none;,在显示时设置为 display: block;,并且会被从顶层辅助功能树中移除/添加。因此,为了使 <dialog> 元素能够动画化,display 属性需要是可动画化的。支持的浏览器通过 离散动画类型 的变体来动画化 display。具体来说,浏览器将在 nonedisplay 的另一个值之间切换,以便动画内容在整个动画持续时间内显示。

所以例如:

  • displaynone 动画到 block(或其他可见的 display 值)时,该值将在动画持续时间的 0% 处切换到 block,使其在整个过程中可见。
  • displayblock(或其他可见的 display 值)动画到 none 时,该值将在动画持续时间的 100% 处切换到 none,使其在整个过程中可见。

注意: 使用 CSS 变换进行动画时,需要设置 transition-behavior: allow-discrete 以启用上述行为。此行为在使用 CSS 动画进行动画时默认可用;不需要等效的步骤。

过渡对话框元素

当使用 CSS 转换来动画化 <dialog> 时,需要以下功能:

@starting-style @规则

提供一组设置在 <dialog> 上的属性的起始值,您希望每次打开时都从这些值进行转换。这对于避免意外行为是必需的。默认情况下,CSS 转换仅在可见元素的属性值从一个值更改为另一个值时发生;它们不会在元素的首次样式更新时触发,也不会在 display 类型从 none 更改为另一种类型时触发。

display 属性

display 添加到 transitions 列表中,以便在转换期间 <dialog> 将保持为 display: block(或对话框打开状态下设置的其他可见 display 值),确保其他转换可见。

overlay 属性

在 transitions 列表中包含 overlay 以确保 <dialog> 从顶层移除被推迟到过渡完成,再次确保过渡可见。

transition-behavior 属性

displayoverlay 过渡(或在 transition 缩写)上设置 transition-behavior: allow-discrete,以启用这两个默认不可动画属性的离散过渡。

这是一个快速示例,展示了它可能是什么样子。

HTML

HTML 包含一个 <dialog> 元素,以及一个显示对话框的按钮。此外,<dialog> 元素还包含另一个用于关闭自身的按钮。

html
<dialog id="dialog">
  Content here
  <button class="close">close</button>
</dialog>

<button class="show">Show Modal</button>
CSS

在 CSS 中,我们包含了一个 @starting-style 块,它定义了 opacitytransform 属性的过渡起始样式,dialog:open 状态的过渡结束样式,以及默认 dialog 状态的默认样式,以便在 <dialog> 出现后过渡回该状态。请注意 <dialog>transition 列表不仅包含这些属性,还包含 displayoverlay 属性,每个属性都设置了 allow-discrete

我们还为 <dialog> 打开时出现在其后面的 ::backdropbackground-color 属性设置了一个起始样式值,以提供漂亮的变暗动画。dialog:open::backdrop 选择器仅在对话框打开时选择 <dialog> 元素的背景。

css
/* Open state of the dialog  */
dialog:open {
  opacity: 1;
  transform: scaleY(1);
}

/* Closed state of the dialog   */
dialog {
  opacity: 0;
  transform: scaleY(0);
  transition:
    opacity 0.7s ease-out,
    transform 0.7s ease-out,
    overlay 0.7s ease-out allow-discrete,
    display 0.7s ease-out allow-discrete;
  /* Equivalent to
  transition: all 0.7s allow-discrete; */
}

/* Before open state  */
/* Needs to be after the previous dialog:open rule to take effect,
    as the specificity is the same */
@starting-style {
  dialog:open {
    opacity: 0;
    transform: scaleY(0);
  }
}

/* Transition the :backdrop when the dialog modal is promoted to the top layer */
dialog::backdrop {
  background-color: transparent;
  transition:
    display 0.7s allow-discrete,
    overlay 0.7s allow-discrete,
    background-color 0.7s;
  /* Equivalent to
  transition: all 0.7s allow-discrete; */
}

dialog:open::backdrop {
  background-color: rgb(0 0 0 / 25%);
}

/* This starting-style rule cannot be nested inside the above selector
because the nesting selector cannot represent pseudo-elements. */

@starting-style {
  dialog:open::backdrop {
    background-color: transparent;
  }
}

注意: 在不支持 :open 伪类的浏览器中,您可以使用属性选择器 dialog[open] 来样式化处于打开状态的 <dialog> 元素。

JavaScript

JavaScript 为显示和关闭按钮添加了事件处理程序,使其在点击时显示和关闭 <dialog>

js
const dialogElem = document.getElementById("dialog");
const showBtn = document.querySelector(".show");
const closeBtn = document.querySelector(".close");

showBtn.addEventListener("click", () => {
  dialogElem.showModal();
});

closeBtn.addEventListener("click", () => {
  dialogElem.close();
});
结果

代码渲染如下:

注意: 因为 <dialog> 每次显示时都会从 display: none 变为 display: block,所以在每次进入过渡时,<dialog> 都会从其 @starting-style 样式过渡到 dialog:open 样式。当 <dialog> 关闭时,它会从 dialog:open 状态过渡到默认的 dialog 状态。

在这种情况下,进入和退出时的样式过渡可能会有所不同。请参阅我们的何时使用起始样式的演示示例以证明这一点。

对话框关键帧动画

使用 CSS 关键帧动画为 <dialog> 制作动画时,与过渡有一些值得注意的区别:

  • 您无需提供 @starting-style
  • 您在关键帧中包含 display 值;这将是整个动画的 display 值,或者直到遇到另一个非 none 的 display 值。
  • 您无需显式启用离散动画;关键帧中没有等同于 allow-discrete 的内容。
  • 您也不需要在关键帧中设置 overlaydisplay 动画处理了 <dialog> 从显示到隐藏的动画。

我们来看一个例子,这样你就能看到它是什么样子。

HTML

首先,HTML 包含一个 <dialog> 元素,以及一个显示对话框的按钮。此外,<dialog> 元素还包含另一个用于关闭自身的按钮。

html
<dialog id="dialog">
  Content here
  <button class="close">close</button>
</dialog>

<button class="show">Show Modal</button>
CSS

CSS 定义了关键帧,用于在 <dialog> 的关闭和显示状态之间进行动画,以及 <dialog> 背景的淡入动画。<dialog> 动画包括动画 display,以确保实际可见的动画效果在整个持续时间内保持可见。请注意,无法动画背景淡出——当 <dialog> 关闭时,背景会立即从 DOM 中移除,因此没有任何可动画的内容。

css
dialog {
  animation: fade-out 0.7s ease-out;
}

dialog:open {
  animation: fade-in 0.7s ease-out;
}

dialog:open::backdrop {
  background-color: black;
  animation: backdrop-fade-in 0.7s ease-out forwards;
}

/* Animation keyframes */

@keyframes fade-in {
  0% {
    opacity: 0;
    transform: scaleY(0);
    display: none;
  }

  100% {
    opacity: 1;
    transform: scaleY(1);
    display: block;
  }
}

@keyframes fade-out {
  0% {
    opacity: 1;
    transform: scaleY(1);
    display: block;
  }

  100% {
    opacity: 0;
    transform: scaleY(0);
    display: none;
  }
}

@keyframes backdrop-fade-in {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 0.25;
  }
}

body,
button {
  font-family: system-ui;
}
JavaScript

最后,JavaScript 为按钮添加了事件处理程序,以实现显示和关闭 <dialog> 的功能。

js
const dialogElem = document.getElementById("dialog");
const showBtn = document.querySelector(".show");
const closeBtn = document.querySelector(".close");

showBtn.addEventListener("click", () => {
  dialogElem.showModal();
});

closeBtn.addEventListener("click", () => {
  dialogElem.close();
});
结果

代码渲染如下:

技术摘要

内容类别 流内容,分区根
允许内容 流内容
标签省略 无,起始标签和结束标签都必须存在。
允许父级 任何接受 流内容 的元素
隐式 ARIA 角色 dialog
允许的 ARIA 角色 alertdialog
DOM 接口 HTMLDialogElement

规范

规范
HTML
# the-dialog-element

浏览器兼容性

另见