attr()在伪元素中拿不到自定义属性值,是因为它仅读取HTML中实际存在的data-*属性,且CSS中attr()参数名必须与HTML属性名完全一致(含data-前缀和大小写);移动端Safari 15.4+、Chrome 84+支持其用于content,但X5内核等旧环境需用CSS变量+JS兜底。

attr() 在伪元素里为什么拿不到自定义属性值
因为 attr() 只读取 HTML 元素上**实际存在的属性**,不是 data-* 属性的“值”,而是属性本身——比如 data-watermark="CONFIDENTIAL",attr(data-watermark) 才能取到字符串;写成 data-watermark-text 或漏掉 data- 前缀,CSS 就返回空字符串或 attr() 降级为默认值(如果写了)。
常见错误现象:::before { content: attr(data-wm); } 但 HTML 是 <div data-watermark="xxx"> → 拿不到值;或者用了 <code>content: attr(watermark)(缺 data-)→ 返回空。
- 必须用
data-*命名,且 CSS 中attr()参数名与 HTML 属性名完全一致(包括大小写) - 移动端 Safari 15.4+、Chrome 84+ 支持
attr()用于content,但不支持用于width、transform等其他属性 - 值中含空格、中文、特殊符号无需转义,但若要参与计算(比如配合
calc()),得靠 JS 预处理或 fallback 方案
用 ::before + attr() 实现可缩放水印文字
纯 CSS 水印需要随容器缩放、避免遮挡正文,关键不是“画满全屏”,而是让单个水印块在视口内重复铺开,且字号随屏幕宽高动态调整。直接写死 font-size: 12px 在 iPhone 和 iPad 上效果天差地别。
实操建议:把水印文本和缩放系数都挂到根元素上,用 attr() 拉文本,用 clamp() 控制字号,再用 background + repeating-linear-gradient 铺底(更轻量,比大量伪元素节点强)。
立即学习“前端免费学习笔记(深入)”;
html <body data-watermark="内部资料" data-scale="0.8"> <div class="content">...</div> </body>
css
body::before {
content: attr(data-watermark);
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
pointer-events: none;
z-index: 1000;
opacity: 0.06;
font-size: clamp(14px, 2.5vw, 36px);
line-height: 1;
transform: rotate(-45deg) translate(-25%, -25%);
transform-origin: center;
background:
repeating-linear-gradient(
0deg,
transparent,
transparent 2em,
#000 2em,
#000 4em
),
repeating-linear-gradient(
90deg,
transparent,
transparent 2em,
#000 2em,
#000 4em
);
background-size: 12em 12em;
color: #000;
}
注意:background 这里只是模拟网格定位参考,真正文字由 content 渲染——这样既保留 attr() 的动态性,又规避了逐个伪元素定位的复杂度。
移动端适配时字体模糊、旋转偏移的修复点
iOS Safari 对 transform: rotate() + 小字号组合渲染异常:文字发虚、边缘锯齿、甚至整体偏移几像素。这不是 attr() 的问题,而是 GPU 合成层未对齐导致的亚像素渲染失效。
- 强制开启硬件加速:
transform: rotate(-45deg) translateZ(0),比单纯translate(-25%, -25%)更稳 - 字号避开小数:用
clamp(16px, 2.4vw, 32px)而非2.5vw,因某些 viewport 下 2.5vw 计算出 15.7px,触发亚像素 - 避免
line-height小数(如1.2),统一设为1或1.0,防止行高叠加造成垂直错位 - 如果水印需响应深色模式,不要依赖
@media (prefers-color-scheme: dark)动态改color——attr()不触发重绘,得用color-scheme: light dark配合currentColor
当 attr() 不够用:JS 注入 + CSS 变量兜底方案
attr() 无法做条件判断、拼接、大小写转换,比如想让水印自动加时间戳、或根据 URL 参数切换内容,就得 JS 干预。
推荐做法:JS 读取逻辑后,把最终字符串写入 style 标签或 :root 自定义属性,CSS 用 var(--watermark) 接入。这样仍保持样式层干净,且兼容性比纯 attr() 更广(IE11 都能跑变量)。
js
const text = new URLSearchParams(location.search).get('env') === 'prod'
? 'PRODUCTION'
: 'TESTING';
document.documentElement.style.setProperty('--watermark', `"${text}"`);
css
body::before {
content: var(--watermark, "DEFAULT");
}
这个组合的关键在于:JS 只负责“生成值”,不碰样式规则;CSS 只负责“消费值”,不碰逻辑。两者边界清晰,维护时谁改谁的模块就行。
最易被忽略的是:iOS 微信内置浏览器(X5 内核)对 attr() 支持极差,哪怕标称 Chrome 80+ 也常返回空;真要全端覆盖,var() + JS 注入是目前最稳的路径。


















