attr()

Baseline 广泛可用 *

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

* 此特性的某些部分可能存在不同级别的支持。

备注: attr() 函数可用于任何 CSS 属性,但除 content 之外的属性支持仍处于实验阶段。

attr() 这个 CSS 函数用于获取所选元素的属性值,并将其用于属性值中,其用法类似于 var() 函数替换自定义属性值。它也可以用于伪元素,在这种情况下,将返回伪元素源元素的属性值。

试一试

blockquote {
  margin: 1em 0;
}

blockquote::after {
  display: block;
  content: " (source: " attr(cite) ") ";
  color: hotpink;
}
<blockquote cite="https://mozilla.org/en-US/about/">
  Mozilla makes browsers, apps, code, and tools that put people before profit.
</blockquote>

<blockquote cite="https://webdev.ac.cn/about/">
  Google believes in an open, accessible, private, and secure web.
</blockquote>

语法

css
/* Basic usage */
attr(data-count)
attr(href)

/* With type */
attr(data-width px)
attr(data-size rem)
attr(data-name raw-string)
attr(id type(<custom-ident>))
attr(data-count type(<number>))
attr(data-size type(<length> | <percentage>))

/* With fallback */
attr(data-count type(<number>), 0)
attr(data-width px, inherit)
attr(data-something, "default")

参数

attr() 函数的语法如下:

attr(<attr-name> <attr-type>? , <fallback-value>?)

参数为:

<attr-name>

应从所选 HTML 元素中检索其值的属性名称。

<attr-type>

指定如何将属性值解析为 CSS 值。它可以是 raw-string 关键字、type() 函数或 CSS 维度单位(使用 <attr-unit> 标识符指定)。如果省略,则默认为 raw-string

raw-string

raw-string 关键字使属性的字面值被视为 CSS 字符串的值,而不进行任何 CSS 解析(包括 CSS 转义、空白移除、注释等)。<fallback-value> 仅在省略属性时使用;指定空值不会触发回退。

css
attr(data-name raw-string, "stranger")

备注: 此关键字最初在 Chromium 浏览器中命名并支持为 string。为了向后兼容,这两个关键字都将在短期内得到支持。

type()

type() 函数接受一个 <syntax> 作为其参数,该参数指定了将值解析成的数据类型。

备注: 出于安全原因<url> 不允许作为 attr() 的数据类型。

<attr-unit>

<attr-unit> 标识符指定数值应具有的单位(如果有)。它可以是 % 字符(百分比)或 CSS 距离单位,如 pxremdegs 等。

css
attr(data-size rem)
attr(data-width px, inherit)
attr(data-rotation deg)
<fallback-value>

当指定的属性缺失或包含无效值时使用的值。

返回值

attr() 的返回值是名为 <attr-name> 的 HTML 属性的值,该值被解析为给定的 <attr-type> 或解析为 CSS 字符串。

当设置了 <attr-type> 时,attr() 会尝试将属性解析为指定的 <attr-type> 并返回它。如果属性无法解析为给定的 <attr-type>,则将返回 <fallback-value>。如果未设置 <attr-type>,属性将被解析为 CSS 字符串。

如果未设置 <fallback-value>,当未设置 <attr-type> 时,返回值将默认为空字符串;当设置了 <attr-type> 时,将默认为保证无效值

描述

限制和安全性

attr() 函数可以引用那些从未打算用于样式化且可能包含敏感信息的属性(例如,页面上脚本使用的安全令牌)。通常情况下,这没有问题,但在 URL 中使用时可能会成为安全威胁。因此,你不能使用 attr() 动态构建 URL。

html
<!-- This won't work! -->
<span data-icon="https://example.org/icons/question-mark.svg">help</span>
css
span[data-icon] {
  background-image: url(attr(data-icon));
}

使用 attr() 的值会被标记为attr()-tainted”(受 attr() 污染的)。将一个 attr()-tainted 值用作或用于 <url> 中,会使声明在计算值时无效(invalid at computed value time),简称 IACVT

向后兼容性

一般来说,现代的 attr() 语法是向后兼容的,因为旧的使用方式——不指定 <attr-type>——与以前的行为相同。在代码中使用 attr(data-attr) 与编写 attr(data-attr type(<string>)) 或更简单的 attr(data-attr string)) 是相同的。

然而,有两种边缘情况,现代 attr() 语法的行为与旧语法不同。

在以下代码片段中,不支持现代 attr() 语法的浏览器会丢弃第二个声明,因为它们无法解析它。在这些浏览器中,结果是 "Hello World"

html
<div text="Hello"></div>
css
div::before {
  content: attr(text) " World";
}
div::before {
  content: attr(text) 1px;
}

在支持现代语法的浏览器中,输出将是……什么都没有。这些浏览器会成功解析第二个声明,但因为它对于 content 属性是无效内容,所以该声明会变得“在计算值时无效”,简称 IACVT

为防止这种情况,建议进行特性检测

第二个边缘情况如下:

html
<div id="parent"><div id="child" data-attr="foo"></div></div>
css
#parent {
  --x: attr(data-attr);
}
#child::before {
  content: var(--x);
}

不支持现代语法的浏览器会显示文本 "foo"。在支持现代 attr() 的浏览器中,则没有输出。

这是因为 attr()——类似于使用 var() 函数的自定义属性——在计算值时进行替换。在现代行为下,--x 首先尝试从 #parent 元素读取 data-attr 属性,因为 #parent 上没有该属性,所以结果为空字符串。然后该空字符串被 #child 元素继承,导致设置了 content: ; 声明。

为防止这种情况,除非你明确希望如此,否则不要将继承的 attr() 值传递给子元素。

特性检测

你可以使用 @supports at-rule 来检测对现代 attr() 语法的支持。在测试中,尝试将一个高级 attr() 赋值给一个(非自定义的)CSS 属性。

例如

css
@supports (x: attr(x type(*))) {
  /* Browser has modern attr() support */
}

@supports not (x: attr(x type(*))) {
  /* Browser does not have modern attr() support */
}

我们可以使用 CSS.supports() 在 JavaScript 中执行相同的检查:

js
if (CSS.supports("x: attr(x type(*))")) {
  /* Browser has modern attr() support */
}

if (!CSS.supports("x: attr(x type(*))")) {
  /* Browser does not have modern attr() support */
}

正式语法

<attr()> = 
attr( <attr-name> <attr-type>? , <declaration-value>? )

<attr-name> =
[ <ident-token>? '|' ]? <ident-token>

<attr-type> =
type( <syntax> ) |
raw-string |
number |
<attr-unit>

<syntax> =
'*' |
<syntax-component> [ <syntax-combinator> <syntax-component> ]* |
<syntax-string>

<syntax-component> =
<syntax-single-component> <syntax-multiplier>? |
'<' transform-list '>'

<syntax-combinator> =
'|'

<syntax-string> =
<string>

<syntax-single-component> =
'<' <syntax-type-name> '>' |
<ident>

<syntax-multiplier> =
'#' |
'+'

<syntax-type-name> =
angle |
color |
custom-ident |
image |
integer |
length |
length-percentage |
number |
percentage |
resolution |
string |
time |
url |
transform-function

示例

content 属性

在此示例中,我们将 data-foo data-* 全局属性的值添加到 <p> 元素内容的开头。

HTML

html
<p data-foo="hello">world</p>

CSS

css
[data-foo]::before {
  content: attr(data-foo) " ";
}

结果

使用回退值

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

在此示例中,我们将 data-browser data-* 全局属性的值附加到 <p> 元素。如果 <p> 元素缺少 data-browser 属性,我们将附加回退值“Unknown”。

HTML

html
<p data-browser="Firefox">My favorite browser is:</p>
<p>Your favorite browser is:</p>

CSS

css
p::after {
  content: " " attr(data-browser, "Unknown");
  color: tomato;
}

结果

color 值

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

在此示例中,我们将 background-color 的 CSS 值设置为分配给 <div> 元素的 data-background data-* 全局属性的值。

HTML

html
<div class="background" data-background="lime">
  background expected to be red if your browser does not support advanced usage
  of attr()
</div>

CSS

css
.background {
  background-color: red;
}

.background[data-background] {
  background-color: attr(data-background type(<color>), red);
}

结果

使用维度单位

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

在此示例中,data-rotation 属性被解析为一个 deg 单位,用于指定元素的旋转角度。

HTML

html
<div data-rotation="-3">I am rotated by -3 degrees</div>
<div data-rotation="2">And I by 2 degrees</div>
<div>And so am I, using the fallback value of 1.5deg</div>

CSS

css
div {
  width: fit-content;
  transform-origin: 50% 50%;
  rotate: attr(data-rotation deg, 1.5deg);
}

结果

attr() 值解析为 <custom-ident>

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

在此示例中,view-transition-name 属性的值来自元素的 id 属性。该属性被解析为 <custom-ident>,这正是 view-transition-name 接受的值类型。

view-transition-name 的结果值是 card-1card-2card-3 等。

HTML

HTML 包含四张具有不同 id 属性的卡片和一个“Shuffle cards”(洗牌)的 <button>,用于打乱卡片顺序。

html
<div class="cards">
  <div class="card" id="card-1">1</div>
  <div class="card" id="card-2">2</div>
  <div class="card" id="card-3">3</div>
  <div class="card" id="card-4">4</div>
</div>
<button>Shuffle cards</button>

CSS

卡片在 flex 容器中布局:

css
.cards {
  display: flex;
  flex-direction: row;
  gap: 1em;
  padding: 1em;
}

在每张卡片上,attr() 函数获取 id 属性并将其解析为 <custom-ident>,用作 view-transition-name 属性的值。如果卡片上未设置 id,则使用回退值 none

css
.card {
  view-transition-name: attr(id type(<custom-ident>), none);
  view-transition-class: card;
}

JavaScript

当按下 <button> 时,卡片会被打乱。这是通过随机化一个包含所有卡片引用的数组的顺序,然后更新每张卡片的 order 属性为其新的数组索引位置来实现的。

为了让每张卡片动画到其新位置,我们使用了视图过渡(View Transitions)。这是通过将 order 的更新包装在对 document.startViewTransition 的调用中完成的。

js
const shuffle = (array) => {
  for (let i = array.length - 1; i >= 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
};

document.querySelector("button").addEventListener("click", (e) => {
  const $cards = Array.from(document.querySelectorAll(".card"));
  shuffle($cards);
  document.startViewTransition(() => {
    $cards.forEach(($card, i) => {
      $card.style.setProperty("order", i);
    });
  });
});

结果

规范

规范
CSS 值和单位模块 Level 5
# attr-notation

浏览器兼容性

另见