文章目录
- 自带交互的details元素
- 自定义样式
- 案例
- 全新name属性
- dialog元素
- form元素与对话框自动关闭
- 关闭来源的判断
- 自动聚焦特性
- showModal()方法与真正的对话框
- 焦点隔离特性
- 顶层特性
- 自动层级特性
- 原生弹层与popover属性
《HTML并不简单:Web前端开发精进秘籍》张鑫旭 笔记
自带交互的details元素
可以实现几乎任意的通过点击行为触发的展开和收起效果。
<details>
元素通常和<summary>
元素同时使用。
<details open>
<summary>这是摘要</summary>
<p>这里具体描述</p>
</details>
点击summary
标签可以展开或收起下面的内容。
对于这段代码:
<details>
元素是整个交互的核心,即使没有设置<summary>
元素,也可以通过切换open
属性决定里面内容的显示与隐藏<summary>
元素表示摘要,是点击行为的触发源,也就是在默认情况下,我们通过点击<summary>
元素来影响交互行为的发生- 若设置
open
属性,则默认展开;未设置默认隐藏 - 如果没有设置
summary
,浏览器会自动补上:Chrome自动生成“详情”,Edge自动生成“详细信息” - 自动创建的
<summary>
元素是写在Shadow DOM
中的,外部CSS无法改变样式 - 在实际开发中,若不想出现summary,可以手动写一个隐藏的
<summary hidden></summary>
作者示例:https://www.htmlapi.cn/3/1-1.html
自定义样式
改变颜色:
summary::marker {
color: pink;
}
改变位置,比如小三角改到右边:
summary {
width: fit-content;
direction: rtl;
}
自定义小三角图标:隐藏浏览器原生的标记符,再使用::before或::after伪元素模拟
案例
details/summary元素实际使用案例 (htmlapi.cn)
- 悬浮菜单,自定义下拉框
- 多项菜单折叠
- 多级嵌套的树形菜单
全新name属性
目前仅Chrome和Safari浏览器支持。
如果多个<details>
元素设置了相同的name属性值,则这些<details>
元素之间会形成互斥关系,即同一时间只有一个<details>
元素可以展开,其他的元素会自动收起。
dialog元素
对话框元素 - HTML(超文本标记语言) | MDN (mozilla.org)
只要没有兼容性方面的顾虑,对话框效果,使用dialog元素实现一定是最佳解决方案
<dialog>
对话框的主体内容可以是任何HTML元素。
<dialog open>
<p>aaaaa</p>
</dialog>
如果我们使用open属性或者使用dialog.open()
方法打开对话框,则对话框默认并不是在屏幕的垂直中心位置,而是在<dialog>
元素所在的DOM树位置附近。
dialog还有一些无可替代的使用特性。
form元素与对话框自动关闭
对话框元素中往往都会有一两个按钮不参与任何业务逻辑处理,其作用仅仅是关闭对话框,例如Alert类型对话框的确认按钮,或者是对话框右上角的图标字样的关闭按钮。
可以直接使用HTML实现这种功能,不需要借助JS。
dialog元素表单提交自动关闭 (htmlapi.cn)
<dialog open>
<p>hello world</p>
<form method="dialog">
<button>我知道了</button>
</form>
</dialog>
点击【我知道了】,对话框会关闭。
关键:在form元素上设置了method='dialog'
,一个表单新特性。声明此属性后,提交表单元素时,不会刷新页面或打开新窗口,而是关闭当前dialog对话框元素。
表单提交的按钮只能是submit性质的按钮。因此,【我知道了】按钮不能设置为type="button"
或者type="reset"
。
button
的type只有submit、button和reset。
只有type='submit'
会关闭对话框。
在type="submit"
上的button
设置属性formmethod="dialog"
,也可以实现关闭对话框功能。
<dialog open>
<p>hello world</p>
<form>
<button formmethod="dialog">我知道了</button>
</form>
</dialog>
关闭来源的判断
<dialog>
元素的DOM对象上支持一个名为returnValue
的属性,可以自动或手动设置对话框关闭行为的来源。
https://www.htmlapi.cn/3/2-2.html
自动聚焦特性
原生HTML元素:无障碍访问天然支持,无需额外的自定义。
所谓“自动聚焦”,指的是对话框显示后,浏览器的焦点会自动聚焦到对话框内。
showModal()方法与真正的对话框
真正的对话框应该是足够聚焦的(用户只能专注于当前的任务),而不是像一个普通的绝对定位浮层。
https://www.htmlapi.cn/3/2-3.html
show
和showModal
方法显示对话框的区别:
show
- 水平居中
- 绝对定位
- 没有背景层
showModal
- 水平垂直居中
- 固定定位
- 有背景层
showModal方法显示对话框的背景层是用CSS伪元素::backdrop
自定义实现的:
dialog::backdrop {
background://可以修改背景
}
可以使用:modal
伪类区分是普通对话框或模态对话框:
dialog:modal {
// 模态对话框的样式
}
除此之外,showModal()
方法还有现有对话框组件无法模拟的稀缺特性:焦点隔离特性 和 自动顶层特性 。
焦点隔离特性
一旦模态对话框显示,Web页面的焦点只能出现在当前的对话框元素中,对话框之外的元素是不可能获取焦点的,哪怕使用JavaScript代码强制聚焦也不行。也就是说整个网页中只有所示三个元素可以获得焦点。
此特性极为稀缺 。如果想用JS模拟,仅存在理论上的可能性,如使用HTML inert
属性让dialog外的所有元素全部禁用。这不优雅简洁。
顶层特性
使用
show()
方法让对话框显示,对话框元素的层级是由CSS样式决定的,如果我们希望对话框元素的层级是最高的,则往往需要设置一个数值较大的z-index
属性值。
使用showModal()
方法让对话框显示,对话框元素的层级自动最高,无需任何CSS样式设置,此特性被称为顶层特性,有个专门的名词描述此特性,叫作top-layer
。它是脱离z-index的。
top-layer顶层特性实现的原理比较简单:直接在<html>
窗体之外创建一个绘制图层,自然可以覆盖页面主体中的所有元素,尽管页面元素z-index为无穷大。
但是,一个页面可能有多个dialog元素。若多个dialog元素同时显示,层级规则该如何呢?
自动层级特性
如果一个页面中有多个对话框通过showModal()
方法显示,那么后显示的对话框层级一定最高。
传统的固定定位元素,如果不专门设置层级,则一定是位于DOM树后面的元素的层级最高,而模态对话框的层级高低与DOM位置无关,与呈现的时机有关。
若是用show
方法,后弹出的dialog可能被覆盖在下面。
是用showModal
方法,后弹出的dialog会在上面。
https://www.htmlapi.cn/3/2-4.html
综上所述,dialog元素不是普通的div元素可以比的。它有非常好用稀缺的特性,可以优雅简洁的实现一些功能。
原生弹层与popover属性
dialog在一些场景也不适用。如:点击预览图片时。
目标:点击图片可以预览图片,点击其他空白地方收起。
若用dialog实现,则需要给按钮绑定点击事件,将图片元素放在dialog中,然后写一个弹窗关闭的方法。麻烦。
在移动端开发中,许多交互效果都是是用弹层 实现的,即点击空白区域隐藏弹层 ,而dialog的模态层(上述为背景层)是没有隐藏行为的。因此这种类似场景不适合是用dialog。
可以使用popover
,一个新HTML属性,已经被各大现代浏览器支持。是一个全局HTML属性,即:所有元素都可以设置的属性,如id、class、elementtiming属性(性能监控相关)。
基本使用:
<button popovertarget="imgPopover">点击我</button>
<div popover id="imgPopover">
<img src="/stone-art.jpg" alt="" />
</div>
https://www.htmlapi.cn/3/2-5.html
popover
弹层元素是顶层元素,且它模态背景是透明可穿透的,可以使用::backdrop
自定义:
[popover]::backdrop {
background-color: #000;
}
popover
弹层内容之外的区域点击后会自动关闭当前弹层,无须额外的DOM事件支持,此行为可以自定义。
popover - HTML(超文本标记语言) | MDN (mozilla.org)
常用移动端弹出层实践:https://www.htmlapi.cn/3/2-6.html
<div class="ui-select">
<button is="ui-select" popovertarget="selectPopover">点击我</button>
<select is="ui-select" popup>
<option>请选择</option>
<option>选项1</option>
<option>选项2</option>
</select>
</div>
<ui-popup-container id="selectPopover" popover>
<form id="form">
<ui-list-item><input type="radio" name="n1" value="请选择" checked data-index="0">请选择</ui-list-item>
<ui-list-item><input type="radio" name="n1" value="选项1" data-index="1">选项1</ui-list-item>
<ui-list-item><input type="radio" name="n1" value="选项2" data-index="2">选项2</ui-list-item>
</form>
</ui-popup-container>
form.onchange = function () {
const input = form.querySelector('input:checked');
const select = document.querySelector('select');
select.selectedIndex = input.dataset.index;
select.dispatchEvent(new Event('change'));
// 关闭弹层
selectPopover.hidePopover();
}