跳转到内容

Modal 模态框组件

模态框组件可以用来快速创建对话框、弹出窗口,灯箱等任何你所需的组件。

组件会在背景组件上层渲染其 children 节点。 模态框提供了一些重要的功能:

  • 💄 管理了当一次只显示一个不能满足时的模态框堆叠。
  • 🔐 创建了一个背景暗化组件,这样能禁用在模态框之外的交互。
  • 🔐 在模态框打开时禁用页面内容的滚动。
  • ♿️ 它妥善管理焦点;移动到模态内容,并保持内容一直存在直到模态框关闭。
  • ♿️ 自动添加适当的 ARIA 角色。
  • 📦 5kB 的压缩包

术语注释。 “模态框”(Modal)这个词有时也被用来指代“对话框”,但是这种用法属于误用。 模态框的窗口描述了 UI 的一部分。 如果一个元素阻挡了用户与应用的其它部分的互动,这个元素就是模态的。

当你创建一个模态对话框时,使用对话框(Dialog)组件比直接使用模态框更佳。 以下的组件将将模态框作为一个低级别的组件运用:

简单的模态框

<button type="button" onClick={handleOpen}>
  Open Modal
</button>
<Modal
  open={open}
  onClose={handleClose}
  aria-labelledby="simple-modal-title"
  aria-describedby="simple-modal-description"
>
  {body}
</Modal>

请注意,您可以通过 outline: 0 属性来禁用模态框的边缘(通常为蓝色或金色)。

过渡动画

通过使用一个过渡组件,您可以给模态框的打开/关闭状态加上动画效果。 此组件应遵守以下条件:

  • 作为模态框的直接子元素。
  • 有一个 in 属性。 这对应于打开/关闭的状态。
  • 当进入过渡时调用 onEnter 回调属性。
  • 当退出过渡完成后应该调用 onExited 回调属性。 这两个回调属性保证了模态框在关闭并展示完过渡动画时,将会移除子内容。

模态框已经内嵌支持 react-transition-group

或者,您也可以使用 react-spring

服务端渲染的模态框

React 不支持服务端渲染的 createPortal() API。 若您想显示模态框,则需要通过 disablePortal 这个属性来禁用 protal 功能:

<Modal
  disablePortal
  disableEnforceFocus
  disableAutoFocus
  open
  aria-labelledby="server-modal-title"
  aria-describedby="server-modal-description"
  className={classes.modal}
  container={() => rootRef.current}
>
  <div className={classes.paper}>
    <h2 id="server-modal-title">Server-side modal</h2>
    <p id="server-modal-description">If you disable JavaScript, you will still see me.</p>
  </div>
</Modal>

设计局限

焦点陷阱

如果用户试图将焦点离开模态框,模态框会将丢失的焦点移回到组件的主体。

这样做的目的是为了无障碍设计,但是可能会造成问题。 如果用户需要与页面的其他部分进行交互,例如当您需要使用聊天窗口时,那么就可以禁用该行为:

<Modal disableEnforceFocus />

无障碍设计

(WAI-ARIA: https://www.w3.org/TR/wai-aria-practices/#dialog_modal)

  • 记得用 aria-labelledby="id..." 属性来指向 Modal 的标题。 此外,您可以使用 aria-describedby="id..." 属性来为 Modal 组件添加一段描述。

    <Modal
    aria-labelledby="modal-title"
    aria-describedby="modal-description"
    >
    <h2 id="modal-title">
      My Title
    </h2>
    <p id="modal-description">
      My Description
    </p>
    </Modal>
  • 这篇 WAI-ARIA authoring practices 里的方法帮助你通过模态窗口里的内容,为最相关的元素设置初始焦点。

  • 请记住,“模态窗口” 覆盖在主窗口或者另一个模态窗口上。 模态框层下的所有层级都是 inert 的。 也就是说,用户不能与当前处于活跃状态下的模态框之外的内容进行交互。 因为这可能会造成冲突行为