你可能不知道的Dialog弹窗

开发 前端
dialog其实我在两三年之前就有研究过,那时还没有这么多新特性,也有可能是研究不精,后来就搁置了,然后最近在项目中用了dialog,无意中又发现了很多有意思的新特性,这意味着,dom 其实也是在不断发展的,有必要把以前已经用过的再翻出来过一遍,说不定还能发现意想不到的结果。

除了有良好的语义外,随着浏览器的不断更新迭代,还出现了许多你可能不知道的特性,快速了解一下吧~

图片

一、打开和关闭方法

首先,在不查阅任何官方文档的情况下,先做一个选择题目

请问:下面哪组方法(打开/关闭)是合法的?

图片

思考 10 秒...

🤔

🤔

🤔

🤔

正确答案是 C,也就是

// 打开弹窗
dialog.show()
// 打开关闭
dialog.close()

很多同学可能会觉得是 A 或者 B,但是官方文档上确是 C,确实是一个令人迷惑的方法。至于原因,有解释说,dialog是存在于页面上的元素,所以打开用show,而关闭表示中断弹窗内的行为,所以用close,只能说有联系,但比较牵强,或许就是规范设计疏忽了,大家记住就好。

还有一个,如果需要弹窗默认显示,大家先猜猜看?

图片

再来思考 10 秒...

🤔

🤔

🤔

🤔

正确答案是 B,也就是

<dialog open></dialog>

🤣是不是又和上面的show()对不上了?

除了dialog元素,details元素的打开属性也是open

二、表单提交特性

弹窗很多时候的作用都是充当表单输入。

比如一个弹窗中需要有一个关闭或者取消按钮

<dialog>
<h3>欢迎关注前端侦探</h3>
<button>关闭</button>
</dialog>

通常情况下,我们可能需要用到前面提到的dialog.close()方法来主动关闭弹窗。

其实,还可以通过表单特性实现这样一个效果,具体做法是:

  1. 在dialog中嵌套一层form
  2. 给form添加一个method=dialog属性

如下

<dialog>
<form method="dialog">
<h3>欢迎关注前端侦探</h3>
<button>关闭</button>
</form>
</dialog>

这样就可以不用绑定额外的事件来实现关闭功能了,效果如下

Kapture 2023-01-14 at 12.52.33

其实原因就在于表单上的method=dialog属性,如果表单在dialog元素中,提交时就会触发关闭对话框

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/form#attr-method

默认情况下,表单里的button都会触发提交,等同于type=submit,但如果设置了type=button或者type=reset,则不会触发表单提交,自然也不会关闭弹窗

<dialog>
<form method="dialog">
<h3>欢迎关注前端侦探</h3>
<button>关闭</button>
<button type="button">不会关闭</button>
</form>
</dialog>

效果如下

图片

另外还有一个小特性,dialog还有一个returnValue属性,可以返回表单中提交按钮的value值

<dialog>
<form method="dialog">
<h3>欢迎关注前端侦探</h3>
<button value="AAA">提交</button>
</form>
</dialog>

在提交后可以打印弹窗的returnValue,如下

图片

image-20230114132406007

不过暂时还没有想到很有用的场景

三、模态窗口特性

弹窗除了可以通过dialog.show()打开之外,还提供了一个模态窗口,方法是

dailog.showModal()

通过这个方法打开的弹窗,会自带一个半透明的背景,并且完全水平垂直居中

图片

这个半透明的背景并不是普通的元素,而是一个叫做::backdrop[1]的伪元素控制的,并且目前只有通过dailog.showModal()这个方法才能生成

要自定义背景也很容易

dialog::backdrop {
background: rgba(255,0,0,.25);
}

这样就变成了半透明红色

图片

如果希望打开弹窗有动画,可以自定义默认样式,通过visibility的方式实现隐藏显示

dialog{
position: fixed;
margin: auto;
inset: 0;
width: fit-content;
height: fit-content;
display: block;
visibility: hidden;
opacity: 0;
transform: translateY(100px);
transition: .2s;
}
dialog[open] {
visibility: visible;
opacity: 1;
transform: translateY(0);
}

效果如下

图片

其实这里我是不太建议动画的,弹窗就是要反应快速,加了动画反而会拖累整体。

另外,还可以通过:modal伪类来区分是普通弹窗还是模态弹窗

dialog:modal{
/*模态弹窗*/
}
dialog:not(:modal){
/*普通弹窗*/
}

这样可以更多进行自定义行为

模态弹窗还有一个非常省心的点,就是无需关注层级。它有一套非常直观的规则,哪个后打开,哪个层级最高,比如这样两个弹窗

<dialog id="dialog2">
...
</dialog>
<dialog id="dialog1">
...
<button type="button">打开弹窗2</button>
...
</dialog>

在不指定层级的情况下,肯定是后面的层级更高,但如果是通过dialog.showModal打开,就非常直观了,后打开的弹窗肯定会覆盖前面的,效果如下

图片

说到这里,我又想到了一些UI组件库里大到可怕的z-index,大概率就是通过计算不断叠加得出的

四、焦点隔离特性

除了上面一些比较直观的特性外,还有一些可能会忽略的,比如焦点的控制。

默认情况下,打开弹窗后会自动聚焦到弹窗内的第一个可聚焦元素上

Kapture 2023-01-14 at 14.34.43

还可以有input输入框

<dialog>
<form method="dialog">
<h3>欢迎关注前端侦探</h3>
<input name="txt">
<button>关闭</button>
</form>
</dialog>

打开弹窗后可以直接输入,无需额外操作,简直不要太方便

图片

注意,注意,注意:经测试发现,如果添加了打开动画,聚焦特性就会失效

当然,这些只是小儿科,一点点额外的 JS 也能解决,下面介绍一个系统级别的焦点隔离特性。什么意思呢?就是说在打开弹窗后,弹窗就成了一个独立的载体,焦点只能在这个范围内移动,也就是说,无论tab键如何切换,焦点不会跑到弹窗外面去,下面是一个对比效果

  1. 普通弹窗效果

图片

  1. 模态弹窗效果

图片

这个效果其实是和新出的inert属性作用比较类似的,但是要比inert出现的更早,也可以说是通过inert属性将这种隔离特性通用化了,让平民老百姓也可以享受这种高级特性。有兴趣的可以访问我之前的这篇文章:快速了解 inert 属性

五、顶层特性 top-layer

最后介绍一个即便是 JS 也无法模拟的系统级新特性,top-layer。

不知道大家有没有遇到这样的问题,有些弹窗由于业务需要,不得已写在了某些容器下面,即便是fixed定位,也会有失效的时候,比如下面这个例子,父容器如果有transform相关属性并且超出隐藏,就会出现这样被裁剪的情形

图片

上面这个例子来源于 xy-ui 中的 dialog 组件[2],后续优化,敬请期待~

在以前,或者说很多框架中,都会想办法把弹窗放到最外层的 body下,这样就不受影响了,比如下面是vue3中的处理方式

<div>
<Teleport to="body"> <!--将子内容传送到body下-->
<dialog></dialog>
</Teleport>
</div>

但现在,有了全新的 top-layer ,一切都好办了,比如下面是一个通过dialog.showModal()打开的弹窗

图片

你会发现,虽然dialog仍然在原来位置上,但真正渲染到了一个#top-layer的层级上,这个层级非常特殊,已经超越了html文档流,可以说是独一档的存在,这样,无论的dialog在什么位置,最后渲染的地方其实都在#top-layer层级上,自然也不会被父容器裁剪被隐藏了,示意如下

图片

是不是和现代框架有些许类似呢?下面是一个弹窗里嵌套另外一个弹窗

<dialog id="dialog1">
...
<button type="button" >打开弹窗2</button>
<dialog id="dialog2">
...
</dialog>
</dialog>

效果如下

图片

其实这也是后打开的弹窗永远要高于之前弹窗的原因,#top-layer是动态创建的,只有打开的弹窗才会渲染到该层级之下。

另外,经测试发现,firefox 以及 safari 虽然在开发者工具上看不到#top-layer,但是dialog表现基本几乎一致,应该是已经成为了标准规范,日后可以放心使用。

六、dom 其实也是在不断发展的

dialog其实我在两三年之前就有研究过,那时还没有这么多新特性,也有可能是研究不精,后来就搁置了,然后最近在项目中用了dialog,无意中又发现了很多有意思的新特性,这意味着,dom 其实也是在不断发展的,有必要把以前已经用过的再翻出来过一遍,说不定还能发现意想不到的结果,下面是要点总结

  1. dialog​ 的打开和关闭方法很迷惑,分别是show() / close()
  2. dialog​中表单元素在添加method=dialog属性之后,只要触发表单提交就会自动关闭弹窗,无需额外 js
  3. 通过showModal()可以打开模态弹窗,并且后打开的弹窗永远比先打开的弹窗层级要高,无需手动计算层级
  4. 打开弹窗会自动聚焦到弹窗内的第一个可聚焦元素,方便快速输入
  5. 模态弹窗的焦点不会聚焦到弹窗外部,这和inert特性比较类似
  6. 模态弹窗其实是渲染到了html文档流之外,一个叫#top-layer的层级上,因此不会受到原父容器的影响
责任编辑:武晓燕 来源: 前端侦探
相关推荐

2012-11-23 10:57:44

Shell

2021-01-05 11:22:58

Python字符串代码

2020-01-29 19:40:36

Python美好,一直在身边Line

2015-08-13 09:03:14

调试技巧

2019-11-20 10:25:06

sudoLinux

2023-02-27 09:20:24

绝对定位CSS

2014-12-08 10:39:15

2019-11-25 14:05:47

Python装饰器数据

2021-07-12 07:59:06

安全 HTML 属性

2018-05-10 11:50:13

Docker容器冷知识

2010-08-06 13:15:35

2010-07-26 13:24:11

2020-05-09 08:48:21

JavaScript原生方法代码

2010-07-21 12:37:11

Linux用户

2015-05-14 15:59:33

DockerLinux容器管理工具

2010-08-10 09:13:49

Linux用户

2011-02-14 16:11:44

2021-12-17 00:10:00

ChromeDevtools功能

2020-03-05 11:10:18

Left join数据库MySQL

2016-09-05 13:14:11

点赞
收藏

51CTO技术栈公众号