可以看到不管是哪种方式唤起的弹窗都会主动聚焦到第一个输入框中,当然也可以通过设置 autofocus
属性来手动指定打开弹窗后需要聚焦到哪一个元素,如果同时给多个元素设置了 autofocus
时,只有第一个设置的元素会生效。
其实不仅仅是打开有两种方式,关闭弹窗也有两种方式:
<dialog />
内部嵌套一层 <form />
,同时设置 method="dialog"
属性,再搭配上一个不设置 type
属性或者 type
属性的值不为 reset
和 submit
的 <button />
就可以关闭了,示例如下:1<dialog open>2 <form method="dialog">3 这是弹窗内容4 <button>关闭弹窗</button>5 </form>6</dialog>
dialog.close()
方法关闭我相信大多数人都会选择第二种,因为第一种不光有局限性,还会增加额外的 DOM 元素,有点得不偿失。
对话框的事件有两个,分别为:cancel
和 close
,是的,没有类似于 open
/show
等这种监听弹窗展示的事件,可能是考虑到弹窗展示这种行为大多数是由程序触发的,在程序内部知道什么时候会打开这个弹窗,而关闭的时机确是不可知的,所以只有关闭的事件而没有打开的事件。
1dialog.addEventListener('close', console.log)2dialog.addEventListener('cancel', console.log)
顾名思义,当对话框被关闭的时候触发,而不论是以哪种方式关闭的弹窗。
当使用 .showModel()
方法打开弹窗,并按下 Esc
按键时则会触发 cancel
事件,同时还将触发 close
事件,而 cancel
事件会在 close
事件之前触发,所以可以在 cancel
事件内部通过调用 event.preventDefault()
来阻止对话框的默认行为(关闭弹窗)。
最后是样式相关的部分,可以给弹窗进入时设置动画样式,因为原生的展示实在是太生硬了,以下代码实现了一个位移显示的动画:
1dialog {2 position: fixed;3 margin: auto;4 inset: 0;5 width: fit-content;6 height: fit-content;7 display: block;8 visibility: hidden;9 opacity: 0;10 transform: translateY(100px);8 collapsed lines
11 transition: .2s;12}13
14dialog[open] {15 visibility: visible;16 opacity: 1;17 transform: translateY(0);18}
除此之外,你还可以通过 ::backdrop
伪类来设置遮罩层的样式,通过 :modal
和使用 :not()
结合 :modal
来设置样式以区分是否包含遮罩层:
1dialog:modal{2 /*模态弹窗, 调用 .showModal() 方法*/3 border-color: #f004}5
6dialog:not(:modal){7 /*普通弹窗, 调用 .show() 方法*/8 border-color: #0f09}10
4 collapsed lines
11/* 可以使用 ::backdrop 来修改遮罩层的样式 */12dialog::backdrop {13 background-color: rgba(255, 0, 0, .5);14}
最后,你还可以使用以下代码结合文中所有的知识点进行学习:
1<!DOCTYPE html>2<html lang="en">3
4<head>5 <meta charset="UTF-8">6 <meta http-equiv="X-UA-Compatible" content="IE=edge">7 <meta name="viewport" content="width=device-width, initial-scale=1.0">8 <title>Document</title>9
10 <style>102 collapsed lines
11 /* 如果设置了打开动画,自动聚焦的特性就会失效 */12 /* dialog {13 position: fixed;14 margin: auto;15 inset: 0;16 width: fit-content;17 height: fit-content;18 display: block;19 visibility: hidden;20 opacity: 0;21 transform: translateY(100px);22 transition: .2s;23 }24
25 dialog[open] {26 visibility: visible;27 opacity: 1;28 transform: translateY(0);29 } */30
31 dialog:modal{32 /*模态弹窗, 调用 .showModal() 方法*/33 border-color: #f0034 }35
36 dialog:not(:modal){37 /*普通弹窗, 调用 .show() 方法*/38 border-color: #0f039 }40
41 /* 可以使用 ::backdrop 来修改遮罩层的样式 */42 dialog::backdrop {43 background-color: rgba(255, 0, 0, .5);44 }45 </style>46</head>47
48<body>49 <input type="text">50 <button onclick="onOpenClick()">打开</button>51
52 <!--53 tabindex 对于 <dialog /> 元素来说是无效的54 或者通过 open 设置为默认打开, 和主动调用 .show() 方法行为一致, 没有模态框55 建议使用 show()/showModal() 来打开模态框, 而不是指定 open 属性,因为对于可访问性来讲,56 调用了 showModal() 的时候, 将会具有隐式的 aria-modal="true" 属性57 当以 showModal() 方式打开弹窗时,可以使用 esc 键来关闭弹窗, 如果打开了多个, 则会依次关闭(关闭最后一个)58 -->59 <!-- <dialog id="dialog" open> -->60 <dialog id="dialog" open>61 <!-- 如果是通过 showModal() 方法打开的弹窗,则会自动聚焦到第一个可交互的元素上 -->62 <!-- 当有多个可交互元素的时候,可在对应的输入框上增加 autofocus 来手动指定需要聚焦到哪个 -->63 <!-- <h1>测试</h1>64 <input type="text" name="input" value="123">65 <input type="text" name="input" value="123">66 <button value="close" onclick="onCloseClick()" autofocus>关闭</button> -->67
68 <!-- 如果是通过 showModal() 方法打开的弹窗,则会自动聚焦到第一个可交互的元素上 -->69 <!-- 当有多个可交互元素的时候,可在对应的输入框上增加 autofocus 来手动指定需要聚焦到哪个 -->70 <form method="dialog">71 <h1>测试</h1>72 <input type="text" name="input" value="123">73 <input type="text" name="input" value="123" autofocus>74 <button value="close">关闭</button>75 </form>76 </dialog>77
78 <script>79 const dialog = document.getElementById('dialog')80
81 function onOpenClick() {82 // dialog.show()83 dialog.showModal()84 }85
86 function onCloseClick() {87 dialog.close();88 // 当使用的是 form{method="dialog"} 的时候, 可以通过 dialog.returnValue 来获取表单的值(input/button上设置的 value 属性)89 // 如果不是,则会回去一个空字符串90 console.log(dialog.returnValue)91 }92
93 dialog.addEventListener('close', (ev) => {94 console.log('close', ev)95 })96
97 // 当键盘按下 esc 的时候, 会触发 cancel 事件, 也会同时触发 close 事件, 但是 cancel 事件可以阻止 close 事件的触发98 // 而且 cancel 事件的触发是在 close 事件之前, cancel 只会由 esc 触发, close 可以由 esc 和 dialog.close() 触发99 dialog.addEventListener('cancel', (ev) => {100 ev.preventDefault() // 阻止 close 事件的触发101
102 console.log('cancel', ev)103 })104
105
106 dialog.addEventListener('open', (ev) => {107 console.log('open', ev)108 })109 </script>110</body>111
112</html>
以上。