在您的网站上创建弹出窗口或模态框可能是一个相当复杂的过程。通常,刚开始时需要编写大量的样板代码。

但真正的挑战在于如何让这些模态框或弹出窗口具备可访问性。

今天,我将向您展示如何使用 `dialog` 标签,通过简单的 HTML、CSS 和 JavaScript 设置来创建具有良好可访问性的模态框/弹出窗口。

先决条件

在开始学习之前,如果您掌握以下基础知识会更有帮助:

  • CSS基础:您应该熟悉常见的 CSS术语和概念(选择器、属性、定位等)。

  • HTML结构:了解元素在 DOM 中的排列方式,将有助于您更好地理解这些示例。

  • JavaScript 和 DOM 基础:虽然本指南使用的 JavaScript 代码量很少,但掌握如何查询和操作 DOM 元素,会让您在尝试各种功能时更加得心应手。

以上这些就是您所需要的全部条件。不需要任何框架,也不需要大量的样板代码,只需要具备核心网页技术的基础知识即可。

目录

如何使用 popover 创建弹出窗口

仅使用 HTML、CSS 和 JavaScript 从零开始创建弹出窗口可能会相当困难。弹出窗口是一种会出现在屏幕上的小对话框,用于显示额外信息或请求用户输入数据。由于它的设计目的是临时使用的,因此用户通常期望在完成与之交互后它就会自动消失,比如当他们按下 Esc 键或点击弹出窗口外的区域时。

通过使用 HTML 的 `popover` 和 `popovertarget` 属性,您可以实现一些内置的可访问性功能及交互行为,例如通过按 Esc 键关闭弹出窗口、轻松地使其消失等。不过,您仍然需要选择合适的语义元素,并测试键盘操作及屏幕阅读器的兼容性。

使用 HTML 设置弹出窗口

为了方便讲解,让我们来看一个常见的网页弹出窗口示例及其应用场景。我们将创建一个导航元素,当用户点击汉堡菜单或菜单列表时,这个导航元素就会显示出来。

首先,您需要在弹出窗口的容器上设置 `popover` 属性。这样,HTML 会将该容器视为一个弹出窗口,并在默认情况下将其隐藏起来。

然后,你需要为那些会触发弹出窗口的元素(比如按钮元素等)设置popovertarget属性,这样这个元素就可以使用popover属性来显示被隐藏的元素。

示例:

<button popovertarget="navbar-menu" id='nav-btn'>打开</button>

<nav id="navbar-menu" popover>
  <a href="#">>首页<./a>
  <a href="#">>关于我们</a>
  <a href="#">>地址信息<>/a>
</nav>

通过上述设置,你就可以得到一个具有多种内置交互功能的弹出窗口了,这些功能包括按下ESC键关闭窗口,以及点击页面上的其他位置来隐藏窗口(只要这些位置不在弹出窗口区域内)。

请注意,仅仅使用popover属性并不能自动使弹出窗口具备无障碍访问功能。你仍然需要使用适当的语义元素,在需要的地方添加可读的标签,并测试键盘操作及屏幕阅读器的兼容性。

如何调整弹出窗口的位置

接下来,你需要将弹出窗口定位到你想要它出现的地方。默认情况下,无论是使用popover属性还是dialog标签创建的弹出窗口,都会被放置在页面的正中心。

这是因为默认情况下,带有popover属性的元素会被设置为fixed定位方式,其inset值为0,因此弹出窗口会位于页面的中心位置;同时,这些元素的边距也被设置为auto,从而保持窗口居中显示。

注意:inset属性实际上表示元素在页面上的上下左右边距。如果你希望元素的四周边距大小相同,就可以使用这个属性。

如果你不希望弹出窗口位于页面中心,可以先将带有popover属性的元素的定位方式设置为absolute,这样就可以将其从页面的常规布局中分离出来:

#navbar-menu {
  position: absolute;
}

接着,你可以将这个元素的边距设置为0,并将其inset值设置为unset

#navbar-menu {
  position: absolute;
  margin: 0;
  inset: unset;
}

完成这些设置后,你就可以将弹出窗口放置在页面的任意位置了。

如何使用position-anchor属性

注意:CSS中的锚点定位功能是一项较新的技术。在实际应用之前,请先检查浏览器对该功能的支持情况;对于不支持该功能的浏览器,也需要提供相应的替代方案。

下一步就是将弹出窗口定位在触发它的元素附近。为此,我们可以使用CSS中的position-anchor属性。

CSS中的position-anchor属性用于指定一个默认的锚点元素,绝对定位或固定定位的元素会“绑定”到这个锚点元素上。这样一来,你就可以仅通过CSS代码,将弹出窗口与页面上的其他元素关联起来。

在我们的例子中,我们使用了一个菜单图标或汉堡菜单,这些元素可以作为弹出窗口来打开或关闭导航栏。我们希望导航栏菜单能够与触发它的菜单图标紧密关联在一起。

为此,你可以在菜单图标上添加anchor-name属性。这个名称前面必须加上双破折号(当然,这个名字可以是任何你想要的内容)。

#nav-btn {
  anchor-name: --nav;
}

position-anchor属性允许你将一个元素绑定到另一个通过锚点名称标识的元素上。一旦完成了这种绑定,你就可以使用anchor()函数来调整该元素的定位方式,使其相对于那个锚点进行对齐——比如将其顶部、底部或中心与锚点对齐。

#navbar-menu {
  position: absolute;
  margin: 0;
  inset: auto;
  position-anchor: --nav;
  top: anchor(bottom);
  right: anchor(right);
}

将你为锚点指定的名称作为position-anchor属性的值,就可以让导航栏元素(在我们的例子中是#navbar-menu)在页面上围绕nav元素的anchor-name进行定位了。在我们的例子中,nav元素的anchor-name就是#nav-btn

随后,anchor()函数会将导航栏元素的顶部位置调整到与菜单右侧对齐的位置。

如何使用dialog标签创建模态窗口

通过在按钮上使用commandcommandfor属性,你可以直接控制

元素的行为。例如,command="show-modal"会将其打开成模态窗口,而command="close"则会关闭它。

普通的弹出窗口与模态窗口是不同的:普通弹出窗口在激活状态下用户仍然可以与其他页面内容进行交互,但模态窗口一旦打开,用户就无法再与页面的其他部分进行交互了——直到用户选择忽略或确认模态窗口的内容,它才会消失。

被打开成模态窗口时,它会显示在浏览器界面的最上层,从而盖住页面的其他内容。在模态窗口打开期间,页面的其余部分将变得不可交互。浏览器还会在模态窗口的背后创建一个::backdrop层,你可以通过样式设置来为这个背景层添加视觉效果。

下面是一个示例:

<button command="show-modal" commandfor="contact-dialog">
    打开模态窗口
</button>

 <dialog id="contact-dialog">
    <button command="close" commandfor="contact-dialog" aria-label="关闭模态窗口">
      关闭模态窗口
    
    
</dialog>

你可以使用command属性来打开或关闭模态窗口,而commandfor属性则可以通过模态窗口的id来指定要操作的模态窗口。

command属性可以接受以下选项之一:

  • show-modal:此选项用于触发模态窗口或对话框打开的元素上。

  • close:将此选项应用于某个元素时,该元素会关闭当前处于打开状态的模态窗口。

在代码示例中,你可以看到标签为open modal的按钮具有command属性,其选项设置为show-modal,而commandfor属性则通过id指定了要操作的对话框元素。

用于关闭模态窗口的按钮在command属性中使用了close选项,因此点击该按钮时会关闭模态窗口。同时,commandfor属性也指明了应该关闭哪个模态窗口。

下面是一个使用commandcommandfor属性创建的模态窗口的演示:

请尝试点击上面Code Pen页面中的“click to pop a modal!”按钮,看看仅通过使用commandcommandfor属性在dialog标签上创建的模态窗口效果。

使用showModal()创建模态窗口

在较旧的浏览器中,可能不支持command API。在这种情况下,你可以使用JavaScript的showModal()方法,因为与commandcommandfor相比,这种方法在许多浏览器中都是可用的。

const btn = document.querySelector("button");
const dialog = document.querySelector("dialog");

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

如何使用show()打开非模态对话框

有时你可能希望创建一个类似于模态窗口的元素,但它能够保持交互性,且不会像commandcommandfor属性那样阻塞页面的其他内容。为此,你可以使用JavaScript中的show()方法来实现这一目标。

const btn = document.querySelector("button");
const dialog = document.querySelector("dialog");

btn.addEventListener("click", () => {
  dialog.show();
});

上述代码示例说明,在对话框打开时,页面的其他部分仍然可以正常交互。这与使用showModal()command="show-modal"打开对话框的方式不同——后两种方法会将对话框作为模态窗口显示,从而导致页面其他内容无法被操作。

注意:请记住,show()并不属于模态窗口,而更类似于一个对话框元素,它不会阻止用户与页面其他部分的交互。

需要注意的事项

在使用popover元素、commandcommandfor属性,或show()方法时,有一些需要注意的地方。遵守这些注意事项有助于你遵循最佳实践。

不要使用Flex或Grid布局

对于带有popover属性的元素以及模态窗口元素,直接应用Flex或Grid等与布局相关的样式是强烈不推荐的。

默认情况下,带有popover属性的元素、具有commandcommandfor按钮属性的元素,以及使用show()方法的元素,其显示状态会被设置为none。这意味着弹出窗口或模态窗口会隐藏在屏幕上。

如果你直接在带有popover属性的元素或模态窗口元素上应用flexgrid布局,就会改变这些元素的默认行为,导致它们始终显示在屏幕上。这样一来,你就无法再让这些模态窗口隐藏起来了。

你可以参考CodePen演示中的示例:

#navbar-menu {
  display: grid;
/* 其他样式定义*/
}

从这个示例中可以看出,即使我们点击菜单按钮试图隐藏导航元素,它仍然会一直显示在屏幕上。

建议的解决方法

在这种情况下,你可以在带有popover属性的元素上使用popover-open伪类,或者直接在对话框元素上使用dialog[open]属性。

伪类会根据元素与页面交互时的状态来改变其样式。因此,在模态窗口处于打开状态时,我们可以为其设置不同的布局。

示例:

/* 带有popover属性的元素 */
#navbar-menu:popover-open {
  display: grid;
  gap: 2rem;
}

/* 使用对话框元素 */
dialog[open] {
  display: grid;
  gap: 2rem;
}

背景滚动功能

在使用

元素时,还需要考虑背景滚动的问题。根据不同的浏览器和平台设置,当模态对话框打开时,页面的背景内容可能仍然可以滚动。如果你想避免这种行为,就可以明确地禁止在对话框打开期间进行背景滚动。

请看一下这个CodePen示例:

当你点击按钮显示模态窗口时,页面上仍然会显示滚动条,你也可以正常滚动页面。

建议的解决方法

为了解决这个问题,我们可以使用has伪类。has可以帮助我们根据子元素的状态来选择父元素。对于根元素或HTML元素来说,你可以检查它是否包含一个打开的模态窗口;如果确实存在,就可以将这个元素的overflow属性设置为hidden。

例如:

html:has(dialog[open]) {
  overflow: hidden;
}

这样,在模态窗口打开时,页面上的滚动条就会被隐藏。需要注意的是,你也可以使用任何父元素来应用has伪类,而不一定是根元素或HTML元素。

::backdrop伪类

如果你想为popover元素或模态窗口设置自定义的背景颜色,就可以使用::backdrop伪类来调整这些元素的外观。

示例:

dialog::backdrop {
  background: rgba(43, 50, 200, 0.4);
}

这样,模态窗口的背景就会采用指定的RGB颜色,并且透明度设置为0.4,从而呈现出半透明的效果。

Related Posts:

Comments are closed.