2026-04-07 16:41:49
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12118
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
本文内容分为两啪,一个是六边形头像效果的实现,而是金字塔布局(又称蜂巢布局)的实现。
不啰嗦,直接看代码和最终实现的效果,同样的,用的是CSS corner-shape属性。
img {
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
width: 150px;
border: 1px solid #0001;
object-fit: cover;
}
实时渲染效果如下:

如果你是手机访问,或者一些很久没升级的国产浏览器,应当看不到效果,可以看下面的截图:

六边形头像的CSS代码是固定的,大家使用的时候直接复制粘贴就好了。
六边形也正好是蜂巢格子的形状,因此,非常适合用来实现金字塔一样的蜂窝布局。
实际上,这种布局在日常开发中也是比较常见的,例如我最近开发的某个页面就有这样的布局:

一般的开发人员遇到这种状况,可能会手工硬搓每个元素的定位,例如,例如匹配第一项元素,让其绝对定位居中,第二行元素保持Flex布局。
.item:first-child {
/* 第一行特殊居中处理 */
position: absolute;
}
其实可以试试Flex倒序排版。
假设HTML结构如下:
<div class="container"> <span>1</span> <span>2</span> <span>3</span> </div>
则可以试试如下所示的CSS:
.container {
--size: 40px;
--gap: 5px;
--offset: calc((2 * var(--size) + var(--gap)) / (-4 * cos(30deg)));
width: 240px;
display: flex;
flex-wrap: wrap-reverse;
direction: rtl;
justify-content: center;
gap: var(--gap);
padding-bottom: calc(-1 * var(--offset));
}
.container > span {
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
width: calc(var(--size) * 2);
margin-bottom: var(--offset);
/* 排序倒序 */
order: calc(-1 * sibling-index());
/* 提示文字居中 */
display: grid;
place-items: center;
background-color: deepskyblue;
color: #fff;
}
此时的渲染效果如下截图所示:

不过Flex倒序只适合三个数量,如果超过,那么这个布局方法就无效了。
下面问题来了,有没有什么办法,无论列表数量多少,自动金字塔布局呢?
有,Grid布局是可以实现这样的效果的。
我们先从最简单三个列表项开始实现,假设HTML代码如下:
<div class="container"> <s></s> <s></s> <s></s> </div>
如下CSS代码就可以有蜂窝布局效果了:
.container {
--size: 40px;
--gap: 5px;
width: 240px;
display: grid;
grid-template-columns: repeat(auto-fit, var(--size));
justify-content: center;
gap: var(--gap);
padding-bottom: calc((2 * var(--size) + var(--gap)) / (4 * cos(30deg)));
outline: 1px dotted;
}
.container > s {
grid-column-end: span 2;
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
/* 垂直方向间隙和gap保持一致 */
margin-bottom: calc((2 * var(--size) + var(--gap)) / (-4 * cos(30deg)));
background-color: deepskyblue;
}
.container > :nth-child(1) {
grid-column-start: 3;
}
.container > :nth-child(2) {
grid-column-start: 2;
}
原理很简单,只需要精确指定每一行第一个元素的grid-column-start值就好了,在Grid布局中,每一行后面的元素只会自动跟随排列的。
如果是三个列表元素,那么第一行的首元素序列是1,因此选择器是:nth-child(1),第二行的首元素序列是2,因此选择器是:nth-child(2),最后一个元素自动跟随,无需专门设置。
实时渲染效果如下:
由于Chrome浏览器支持了if函数,因此,纯CSS实现不限数量全自动蜂巢布局成为了可能,具体实现代码如下:
@property --_n {syntax: "<integer>";initial-value: 1;inherits: true}
@property --_i {syntax: "<number>";initial-value: 1;inherits: true}
@property --_j {syntax: "<number>";initial-value: 1;inherits: true}
@property --_c {syntax: "<number>";initial-value: 1;inherits: true}
@property --_d {syntax: "<number>";initial-value: 1;inherits: true}
.container {
--s: 40px; /* 尺寸大小 */
--g: 5px; /* 间隙大小 */
display: grid;
grid-template-columns: repeat(auto-fit, var(--s) var(--s));
justify-content: center;
gap: var(--g);
padding-bottom: calc((2 * var(--s) + var(--g)) / (4 * cos(30deg)));
container-type: inline-size;
}
.container > * {
grid-column-end: span 2;
aspect-ratio: cos(30deg);
border-radius: 50% / 25%;
corner-shape: bevel;
margin-bottom: calc((2 * var(--s) + var(--g)) / (-4 * cos(30deg)));
--_n: round(down, (100cqw + var(--g)) / (2 * (var(--s) + var(--g))));
--_i: calc((sibling-index() - 2 + (var(--_n) * (3 - var(--_n))) / 2) / (2 * var(--_n) - 1));
--_c: mod(var(--_i), 1);
--_j: calc(sqrt(2 * sibling-index() - 1.75) - .5);
--_d: mod(var(--_j), 1);
grid-column-start:
if(
style((--_i >= 1) and (--_c: 0)): 2;
style(--_d: 0): max(0, var(--_n) - var(--_j));
);
}
先是根据容器尺寸和元素尺寸计算每行可以显示的数量,然后根据取模的值是不是整数,判断是不是每一行的第一项,通过if()函数设置精准的grid-column-start值。
原理虽然简单,但是实现细节还是很复杂的,比如大家无需深究,直接复制粘贴代码使用就可以了。
只需要将子元素换成图片元素,就可以轻松实现下图所示的蜂巢头像布局效果。

具体不展开,因为受制于兼容性限制,目前只能实验环境使用。
前端三剑客中,CSS的发展是最快的,你看我写的新特性介绍文章,大多数都是CSS,并不是我刻意挑选,而真TM就是大多数前端新特性都是CSS。
考虑到CSS的学习热潮早就沉寂多年。
我觉得CSS这门语言离断层不远了,只要几年不关注,我跟大家讲,那些前沿的CSS代码,绝对是看不懂的。
各种新函数、属性还有语法糖层出不穷,就好比本文这个金字塔蜂巢布局中的CSS实现细节,我估计9成以上的前端是看不懂什么意思的。
其中出现的这些特性,我之前都有介绍:
corner-shape见此文:大开眼界的CSS corner-shape属性
aspect-ratio见此文:Chrome 88已经支持aspect-ratio属性了,学起来
round()、mod()等数学函数:Chrome也支持round等CSS数学函数了
cos()三角函数见:CSS sin()/cos()等数学三角函数简介与应用
sibling-index()索引序号函数介绍出处:CSS索引和数量匹配函数sibling-index sibling-count简介
if()函数介绍:CSS倒反天罡居然支持if()函数了
container-type和100cqw属于容器查询里面的知识:2022年最期待的CSS container容器查询
所以还是那句话,学习是不能停止的,时代变化很快,要是安于现状,说不定就会掉队。
参考文章:响应式金字塔网格
😉😊😇
🥰😍😘
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12118
(本篇完)
2026-03-30 14:08:36
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12115
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
CSS corner-shape属性去年8月份刚刚介绍过,可以实现多种图形效果。

很强,也大开眼界。
有兴趣的可以去这里学习其语法:“大开眼界的CSS corner-shape属性”
但是这些图形效果一次只能创建一个,如果可以将这些图形效果批量复制,岂不是可以实现各种复杂的底纹背景效果。
还真可以实现。
这种将HTML内容变成SVG背景图的技术我之前就研究并介绍过,可以参见“如何让文字作为CSS背景图片显示”此文。
对于文字,我们可以使用纯SVG语法。
但是,对于相对有些复杂的图形效果,我们可以借助<foreignObject>元素。
foreignObject我之前也介绍过,可以用来实现DOM截图效果,详见“SVG foreignObject简介与截图等应用”一文。
所以,我们的实现模板就变成了这样:
.template {
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">对该DIV设置样式</div></foreignObject></svg>'
}
在过去,我们要实现网格线,需要使用两个repeating-linear-gradient()渐变函数,函数里面也需要写比较精确的断点。
如果,借助corner-shape属性,我们有了更加渐变的实现方法。
CSS代码示意:
.grid-bg {
aspect-ratio: 1;
background: #fff url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="border: 1px solid deepskyblue;border-radius: calc(50% - 1px);corner-shape: notch;aspect-ratio: 1;width:60px;"></div></foreignObject></svg>');
background-size: 60px 60px;
}
此时,我们给页面添加一个类名是grid-bg的canvas元素,我们就可以看到如下图所示的底纹效果了:
而CSS的背景图是可以无限叠加的,所以,我们可以再网格线基础上再融合点其他图形,例如,闪烁星星,于是:
..grid-bg2 {
aspect-ratio: 1;
--url-star: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='100%' height='100%'><div xmlns='http://www.w3.org/1999/xhtml' style='background:deeppink;width:60px;height:60px;corner-shape:superellipse(-2.5);border-radius:50%;'></div></foreignObject></svg>");
--url-grid: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="border: 1px solid deepskyblue;border-radius: calc(50% - .75px);corner-shape: notch;aspect-ratio: 1;width:60px;"></div></foreignObject></svg>');
background: var(--url-star) 1px 1px, var(--url-grid);
background-size: 60px 60px;
}
可以有如下图所示的渲染效果:

这里有个codepen地址,里面有多个使用corner-shape属性实现的背景纹理图。

原理都是一样的,我就不赘述了。
如果遇到SVG图形直接访问是可以的,但是作为background图片就无法渲染,试试对其进行转义,代码如下:
const encodeSvg = function (str) {
return "data:image/svg+xml," + str.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E");
}
其中 str就是完整的SVG代码。
另外,需要注意的是,使用<foreignObject>元素作为SVG背景图形的时候,里面的HTML祖先元素需要设置xhtml的命名空间,外部的SVG元素也需要SVG的命名空间,否则会有渲染问题。
截止到今天,也就是2026年3月30日,corner-shape属性依然只有Chrome浏览器支持,所以,本技术,大家目前了解下即可,实际生产环境使用,还需要些时日。

😉😊😇
🥰😍😘
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12115
(本篇完)
2026-03-17 16:25:58
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12112
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
时间如斯,一转眼,做前端开发已经十五六年了,刚开始那会儿,实时通信还是使用轮询、长轮询,后来就是 WebSocket,然后现在又出来了个WebTransport。
WebSocket虽然可以解决大部分的问题,但是并不完美,例如队头阻塞、只能单一流传输、网络切换就断连,尤其是做实时游戏、直播推流这类对延迟要求极高的场景,总觉得差点意思。
所以就有了 WebTransport API,特别使用用在高并发、低延迟的实时场景。
WebTransport 是基于 HTTP/3 + QUIC 协议的新一代实时通信 API,主打一个“低延迟、高吞吐、高灵活”,专门解决 WebSocket 搞不定的那些场景。
我做了个简单的对比表,大家一看就明白:
| 对比维度 | WebSocket | WebTransport |
|---|---|---|
| 协议基础 | 基于 HTTP/1.1 Upgrade,底层是 TCP | 基于 HTTP/3,底层是 QUIC(基于 UDP) |
| 连接建立 | TCP 三次握手,延迟较高 | QUIC 0-RTT/1-RTT 快速握手,最快100ms内建立 |
| 传输模式 | 单一可靠流,只能双向传输 | 可靠流 + 不可靠数据报,支持单向/双向、多路复用 |
| 队头阻塞 | 存在,一个包丢失,后续所有包都要等重传 | 无,单个流阻塞不影响其他流 |
| 网络切换 | 断开连接,需重新握手 | 支持连接迁移,Wi-Fi 切4G也不中断 |
| 适用场景 | 普通实时聊天、简单消息推送 | 实时游戏、直播推流、实时协作、高频数据传输 |
这里需要补充一点:
不是说 WebSocket 不好用了,而是场景不同,选择不同。
如果你的项目只是简单的聊天功能,WebSocket 足够用,没必要强行上 WebTransport;
但如果涉及到高频数据传输(比如游戏里的玩家位置更新)、低延迟要求(比如直播弹幕实时推送),WebTransport 就是最优解。
这就像我们做布局,简单布局用 Flex 就够,复杂布局才需要 Grid,因地制宜最重要。
WebTransport 的核心优势,都源于它的底层协议,但我们前端不用去深究 QUIC 协议的细节,只要掌握它的3个核心特性,就能应对大部分开发场景。
这是 WebTransport 最核心的亮点,也是和 WebSocket 最大的区别——它支持两种传输方式,可根据需求灵活选择:
比方说一个实时多人小游戏,玩家的位置更新不需要100%到达(偶尔丢一个包不影响体验),但聊天消息必须可靠到达。
如果用 WebSocket,只能用一种传输方式,要么牺牲延迟,要么牺牲可靠性。
而用 WebTransport,就能给位置更新用不可靠数据报,聊天消息用可靠流,完美兼顾。
数据传输示意图如下:

WebSocket 是“单一流”传输,也就是说,一个 WebSocket 连接里,所有数据都在一条流里传输,一旦某个数据包丢失,后续所有数据都要等它重传,这就是“队头阻塞”。
而 WebTransport 支持多路复用,一个连接里可以同时创建多个独立的流,每个流互不影响。
比如你做一个直播平台,视频流、音频流、弹幕流可以用不同的流传输,就算视频流出现丢包重传,也不会影响弹幕的实时推送。
这一点,在高并发场景下,体验提升非常明显。
这个特性可能很多同学没意识到它的重要性,但做移动端项目的同学一定懂:
用户用手机浏览网页时,经常会在 Wi-Fi 和 4G/5G 之间切换,这时候 WebSocket 连接会直接断开,需要重新握手建立连接,导致数据中断(比如直播卡顿、游戏掉线)。
WebTransport 基于 QUIC 协议,用“连接ID”来标识连接,而不是 IP 地址,所以就算网络切换,连接也能无缝迁移,数据不会中断。
下面给大家讲解 WebTransport 的核心用法,包括从连接建立,到两种传输模式的使用,每一步都有注释,供大家学习参考。
建立连接很简单,用 WebTransport 构造函数,传入服务器地址,等待 ready 状态即可。这里要注意,服务器地址必须是 https 开头,并且要指定端口(比如 4433)。
// 建立 WebTransport 连接
async function createWebTransport() {
// 服务器地址(必须是 HTTPS,端口可自定义)
const url = 'https://example.com:4433/transport';
try {
// 创建 WebTransport 实例
const transport = new WebTransport(url, {
// 可选:证书指纹,用于验证服务器身份(防止中间人攻击)
serverCertificateHashes: [
{
algorithm: 'sha-256',
value: new Uint8Array([/* 服务器证书指纹 */])
}
]
});
// 等待连接就绪(ready 是一个 Promise)
await transport.ready;
console.log('WebTransport 连接成功');
// 监听连接关闭事件
transport.closed.then(() => {
console.log('WebTransport 连接关闭');
}).catch((err) => {
console.error('WebTransport 连接异常关闭:', err);
});
return transport;
} catch (err) {
console.error('WebTransport 连接失败:', err);
throw err;
}
}
可靠流分为“双向流”和“单向流”,双向流是客户端和服务器可以互相发送数据,单向流是只能一方发送、另一方接收。实际开发中,双向流用得最多(比如聊天)。
// 双向可靠流示例(客户端 ↔ 服务器)
async function useBidirectionalStream(transport) {
// 创建双向流
const stream = await transport.createBidirectionalStream();
// 发送流(客户端 → 服务器)
const writable = stream.writable;
const writer = writable.getWriter();
// 发送文本数据(需要先编码为 Uint8Array)
const encoder = new TextEncoder();
await writer.write(encoder.encode('Hello WebTransport!'));
// 发送完成后关闭写入流
await writer.close();
// 接收流(服务器 → 客户端)
const readable = stream.readable;
const reader = readable.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break; // 接收完成
console.log('收到服务器消息:', decoder.decode(value));
}
}
不可靠数据报的用法更简单,不需要创建流,直接通过 datagrams 属性发送和接收数据,适合高频、非关键数据的传输。
// 不可靠数据报示例(适合高频数据)
async function useDatagram(transport) {
// 发送数据报(客户端 → 服务器)
const writer = transport.datagrams.writable.getWriter();
const encoder = new TextEncoder();
// 模拟高频发送(比如游戏玩家位置)
setInterval(async () => {
const position = { x: Math.random() * 100, y: Math.random() * 100 };
await writer.write(encoder.encode(JSON.stringify(position)));
}, 33); // 30 FPS,和游戏帧率同步
// 接收数据报(服务器 → 客户端)
const reader = transport.datagrams.readable.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const data = JSON.parse(decoder.decode(value));
console.log('收到位置数据:', data);
}
}
这里提醒大家一句:不可靠数据报不保证数据到达,所以不要用它传输重要数据(比如支付信息),否则会出现数据丢失的问题。
// 完整实战代码
async function webTransportDemo() {
try {
// 1. 建立连接
const transport = await createWebTransport();
// 2. 同时使用双向流和数据报
useBidirectionalStream(transport);
useDatagram(transport);
// 3. 关闭连接(按需调用)
// setTimeout(() => {
// transport.close();
// console.log('主动关闭连接');
// }, 10000);
} catch (err) {
console.error('WebTransport 实战失败:', err);
}
}
// 执行 demo
webTransportDemo();
最后,再给大家做个总结,帮大家理清 WebTransport 的适用场景,避免盲目使用。
如果你遇到以下场景,强烈建议用 WebTransport:
如果只是简单的实时聊天、消息推送,WebSocket 已经足够用,没必要强行上 WebTransport——技术选型的核心是“合适”,而不是“最新”。
另外,WebTransport 必须在 HTTPS 环境下使用(本地开发可以用 localhost),目前主流浏览器(Chrome 97+、Firefox 114+、Safari 26.4+)都已支持,不过离在正式环境使用还需要一两年的缓冲时间,除非你的项目不需要管Safari浏览器。

前端技术更新很快,我们不用追求掌握所有新特性,但对于那些能解决实际痛点、提升开发效率的技术,多花点时间吃透,总能在项目中发挥作用。
希望这篇文章能帮大家快速上手 WebTransport,少踩坑、多提效。
对了,提一嘴:本文的原理示意图和代码按钮都是AI生成的,仅供参考!
😉😊😇
🥰😍😘
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12112
(本篇完)
2026-03-09 17:31:56
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12102
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
不知道大家用过<dialog>元素没有,我是使用了很多年了,挺好用的。
对于基础特性,各大浏览器都已经支持不错了。
但是,还不完美,且看下面这个场景。
请问,如果我们希望<dialog>弹框模态显示的时候,点击后面的半透明黑色蒙层关闭弹框,请问如何实现?
在之前,这个需求需要用到JS,但是现在,只需要一个HTML属性就可以实现了,这个属性就是closedBy属性。
比方说下面这个弹框HTML元素:
<dialog closedBy="any">点击蒙层我会隐藏哦~</dialog>
此时,如果我们执行dialog.showModal()让弹框显示,那么点击后面的蒙层弹框就会自动关闭。
眼见为实,你可以试试点击下面的按钮元素(Safari暂不支持),体验我所描述的效果:
closedby 属性是一个枚举属性,支持以下三个关键值:
| 属性值 | 描述 | 关闭方式 |
|---|---|---|
| any | 全部允许 | 点击对话框外部(背景)、按 Esc 键或者调用 close() 方法。 |
| closerequest | 需要请求 | 按 Esc 键、调用 close() 方法;但不能通过点击外部关闭。 |
| none | 禁止自动关闭 | 只能通过 JavaScript 的 close()方法或表单提交关闭(我的书《HTML并不简单》有过具体介绍);Esc 和点击外部均无效。 |
如果我们没有设置closedBy属性,浏览器会当做auto处理。
也就是:
showModal()方法打开的,那么等同于设置了closedBy="closerequest",也就是按下ESC键可以关闭,但是点击蒙层不行;show()方法打开的,那么弹框的关闭行为等同于设置了closedBy="none"。另外,closedBy属性也支持在DOM API层面直接读写,例如:
// 获取closedBy的属性值 (注意驼峰命名) console.log(dialog.closedBy); // 设置closedBy属性 dialog.closedBy = 'none';
最后看下兼容性:

目前Safari浏览器并不支持,若想在实际项目中使用,可以引入Polyfill:https://github.com/tak-dcxi/dialog-closedby-polyfill
使用非常简单:
import { apply, isSupported } from "dialog-closedby-polyfill";
if (!isSupported()) {
apply();
}
其他就没什么了吧,感谢阅读,我们下篇文章再见!

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12102
(本篇完)
2026-03-04 11:36:13
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12089
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
之前“该使用原生popover属性模拟下拉了”这篇文章有介绍过点击行为驱动的popover下拉。
最近发现,鼠标hover悬停也支持popover交互了。
且功能比点击更丰富,适用范围更广,那就是将popovertarget属性换成interestfor属性。
先看案例,HTML如下:
<button interestfor="imgBook">Hover显示图片</button> <img id="imgBook" popover src="book.jpg" />
无需任何JS代码,鼠标经过按钮,就可以让图片显示,实时效果如下(需要Chrome 142+浏览器):
Nice!

popovertarget属性仅适用于<button>元素,但是interestfor属性不仅可以用在按钮元素上,也可以用在各类链接元素上,例如<a>元素、<area>元素。
这个不难理解,<a>元素本身就有点击行为,和popovertarget的点击行为是冲突的。
但是interestfor属性是鼠标经过进入行为,并不会和<a>元素本身的链接跳转想冲突。
例如:
<a href interestfor="myAccount">Hover显示内容</a> <div id="myAccount" popover>我的抖音:“张鑫旭本人”</div>
悬浮上面的链接元素,就可以在显示器的最中间看到类似下面截图的效果了:

除了HTML属性interestfor设置这种交互效果,我们还可以再JavaScript层面,使用DOM的interestForElement直接设置,代码示意:
const invoker = document.querySelector("button");
const popover = document.querySelector("div");
invoker.interestForElement = popover;
此时,Hover button元素也会触发popover变量元素的状态变化。
在传统的popovertarget交互场景下,目标元素需要设置popover属性才可以(默认隐藏,点击显示)。
但是interestfor指向的目标元素是任意的,也就是你就是个普通的元素也是可以的,无需非要绝对定位。
假设有如下所示的HTML代码:
<a href interestfor="markTarget">Hover Me!</a>
<p id="markTarget">鼠标经过链接后我高亮</p>
<style>p:interest-target {
background-color: yellow;
}</style>
此时,经过链接元素,你就会看到<p>元素背景高亮了。
实时渲染效果如下:
鼠标经过链接后我高亮
上面的案例中出现了个CSS新特性,:interest-target伪类,专门用来匹配interestfor匹配元素激活的状态。
其实除了:interest-target伪类,还有个名为:interest-source的CSS伪类。
:interest-source伪类匹配按钮、链接元素处于interest状态的场景。
:interest-target伪类匹配的是目标元素。
我们再来看一个:interest-source伪类应用的按钮,也就是浮层显示的时候,让按钮高亮。
测试代码为:
<button class="mybook" interestfor="mybook">Hover图片显示后,按钮高亮</button>
<img id="mybook" popover src="book.jpg" />
<style>
.mybook:interest-source {
box-shadow: inset 0 0 0 9em yellow;
}</style>
实际效果如下(移动端和非Chrome浏览器可能看不到效果):

popover默认是居中定位的,如果我们希望相对于触发的按钮或链接元素,我们可以使用CSS锚点定位,详见此文“新的CSS Anchor Positioning锚点定位API”。
无需任何JS的参与。
现在的CSS是越来越强大了,唯一的遗憾就是此特性的兼容性还不是很好,目前只有Chrome浏览器支持。

总之,我是非常期待这个CSS特性能够快速全面支持的。
好吧,就介绍这么多,还是挺实用的一个特性。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12089
(本篇完)