直接写:target { opacity: 1; } 不会淡入,因为transition需属性值变化触发,而:target仅匹配状态;必须设默认opacity: 0、visibility: hidden,并在:target中改为opacity: 1、visibility: visible,且transition定义在常态规则中。

为什么直接写 :target { opacity: 1; } 不会淡入?
因为 :target 是状态匹配,不是事件监听——它只在 URL 哈希变化后“立刻生效”,而 CSS transition 必须由属性值变化触发。如果弹窗默认就是 opacity: 1,那匹配 :target 时值没变,transition 就不启动。
必须让初始态和 :target 态的 opacity 值不同,且 transition 定义在常态规则里:
- 弹窗容器默认设
opacity: 0; visibility: hidden; - 同时写
transition: opacity 0.3s ease, visibility 0.3s step-end;(注意:visibility 用step-end防止淡出末尾闪一下) -
#modal:target { opacity: 1; visibility: visible; }—— 这才是触发过渡的“变化”
:target 弹窗必须配 position: fixed 吗?
强烈建议配,原因很实际:
- 不设
position: fixed或absolute,弹窗仍参与文档流,:target匹配后可能把下方内容往下顶,造成页面抖动 - 移动端 Safari 在哈希跳转后若页面已滚动,
:target样式有时延迟重绘,fixed 定位能绕过部分重排问题 - 遮罩层(如全屏
.close)必须position: fixed才能覆盖整个视口,否则滚动后就失效
顺带一提:z-index 要显式设高值(比如 z-index: 1000),别依赖默认堆叠上下文。
立即学习“前端免费学习笔记(深入)”;
点击遮罩关闭弹窗,纯 CSS 能做到吗?
能,但有硬约束:遮罩必须是带 href="#" 的 <a> 标签,且目标 id 不能指向任何真实元素。
- 写法示例:
<a href="#" class="close"></a>,配合 CSSa.close:target { display: none; }不行——#不匹配任何 id,:target永远不生效 - 正确做法:遮罩
<a href="#close">,再加一个空的<div id="close"></div>(或复用其他无内容元素),这样哈希变#close就能触发#close:target,从而让弹窗容器失配:target,回归默认隐藏态 - 注意:iOS Safari 旧版本(≤15.4)在快速连点遮罩时可能丢掉一次哈希变更,导致关不掉——加个
pointer-events: none到遮罩上反而更稳
移动端 Safari 淡入卡顿、闪一下,怎么调?
这不是你代码写错了,是渲染机制差异:
- 避免在
:target规则里写transform: scale()或translateY(),iOS Safari 对哈希驱动的 transform 动画支持不稳定 - 改用
top/left+opacity组合,transition 写成transition: opacity 0.25s ease, top 0.25s ease; - 如果弹窗内含文字,加
will-change: opacity;到弹窗容器,提前提示合成层提升 - 最易忽略的一点:确保
<meta name="viewport">里没禁用缩放(如user-scalable=no),某些 iOS 版本下这会干扰哈希变更的重绘调度


















