1. 简介
本节为非规范性内容。
注意: 当前,本规范仅定义了自定义函数, 它们作用于 CSS 值级别。 预计未来还会定义“混入”(mixin), 即作用于样式规则级别的函数。
自定义属性赋予作者极大的能力, 可以在一个地方定义有用且有时很复杂的值, 然后在样式表中多次复用。 这些值可以在文档中变化, 或根据媒体查询或其他条件而变化, 使其非常灵活和响应式。
然而,这些值在定义时是固定的, 只能通过完全覆盖其先前定义来更改: --shadow: 2px 2px var(--shadow-color) 的声明 其 --shadow-color 值来自于声明该属性的元素, 并且后续对子元素上的 --shadow-color 的更改 并不会影响 --shadow 的值; 它们仍然使用定义 --shadow 时的阴影颜色。 这常常让大量使用复合变量的作者感到困惑。
自定义函数为作者带来了与自定义属性 相同的能力, 但可参数化: 它们具有与 自定义属性 定义相同的灵活性和条件性, 但可以在使用点从其他自定义属性 (或显式作为参数)获取值。 例如,与 --shadow 自定义属性相比, 可以定义 --shadow() 自定义函数, 如下所示:
@function --shadow ( --shadow-color <color> : inherit) { /* 如果未传递 --shadow-color 参数, 或未能成功解析为 <color>, 则尝试使用元素上的 --shadow-color 属性 */ /* var(--shadow-color) 指的是参数 --shadow-color, 而不是自定义属性, 但依然可以像平常一样使用后备值 */ result:2 px 2 px var ( --shadow-color, black); } .foo{ --shadow-color : blue; box-shadow : --shadow (); /* 生成蓝色阴影 */ /* 或直接这样 */ box-shadow:--shadow ( blue); }
2. 定义自定义函数
可以将自定义函数 视为一种高级自定义属性, 它不是用单一固定值替换, 而是根据函数参数和调用时的自定义属性值计算其替换值。 与 var() 语法 用于自定义属性替换不同,自定义函数通过 <dashed-function> 语法调用, 允许传递额外的参数值。
@function --negative ( --value) { result : calc ( -1 *var ( --value)); }
然后,可以在某个声明中通过 --negative() 调用该函数:
html{ --gap : 1 em ; padding : --negative ( var ( --gap)); /* 或直接传值,如: */ padding:--negative ( 1 em ); }
<dashed-function> 是一种任意替换函数, 类似于 var()。 它们出现在属性值中时, 解析时会被视为有效, 只会在计算值阶段才会实际求值和解析, 即在任意替换发生后。
2.1. @function 规则
@function 规则定义了一个自定义函数, 由名称、 参数列表、 函数体, 以及可选的由语法定义描述的返回类型组成。
每个函数参数包含一个名称(<custom-property-name>); 可选的参数类型,由语法定义描述; 以及可选的默认值。
<@function> = @function <function-token> <function-parameter>#? ) [ returns <css-type> ]? { <declaration-rule-list> } <function-parameter> = <custom-property-name> <css-type>? [ : <declaration-value> ]? <css-type> = <syntax-component> | <type()> <type()> = type( <syntax> )
2.1.1. 函数前言
<function-token> 产生式 必须以两个连字符(U+002D HYPHEN-MINUS)开头, 类似于 <dashed-ident>, 否则定义无效。
最终自定义函数的名称由 <function-token> 的名称给出, 可选的函数参数由 <function-parameter> 的值给出 (默认为空集合), 可选的返回类型由紧跟returns关键字的 <css-type> 给出 (默认为 type(*))。
@function --foo ( --a <length>) { /* ... */ } @function --foo ( --a <color>) { /* ... */ } @function --foo ( --a <length>+) { /* ... */ }
但如果<syntax> 需要<syntax-combinator>,则必须用 type() 包裹:
@function --foo ( --atype ( <number> | <percentage>)) { /* ... */ }
@function 规则的名称是树作用域名称。 如果同名下存在多个 @function, 则级联层级更高的获胜, 同一层内后定义的获胜。
如果函数参数包含同一个 <custom-property-name> 多次, 则该 @function 规则无效。
2.1.2. 函数体
@function 规则的主体接受条件分组规则, 如 @media。 另外,还接受以下描述符:
未知描述符会被视为无效并忽略, 但不会导致 @function 规则本身无效。
2.2. result 描述符
名称: | result |
---|---|
适用规则: | @function |
值: | <declaration-value>? |
初始值: | n/a(见正文) |
result 描述符 定义了由其 @function 规则定义的自定义函数 求值的结果。 使用 var() 函数, 可以引用函数参数、局部变量, 以及通过 <dashed-function> 形式调用的其它 自定义函数。
result 描述符本身没有类型, 但其 解析后的值会在 替换 <dashed-function> 时进行类型检查。
2.3. 参数与局部变量
本节为非规范性内容。
在自定义函数的 函数体内部, var() 函数可以访问局部变量(即在 函数体中定义的自定义属性), 函数参数 (传递给函数的值,或设为默认值), 以及调用点(元素或另一个 自定义函数)定义的 自定义属性。
在上述列表中,前面的内容会“覆盖”同名的后面内容——如果有一个名为 --foo 的局部变量,那么 var(--foo) 会被该 局部变量 替换, 而不是参数或者外部定义的自定义属性。 但其他值仍可以访问: 将 --foo 局部变量设置为 initial 时,会解析为 --foo 参数, 设置为 inherit 时, 则解析为调用点的 --foo 自定义属性。
@function --outer ( --outer-arg) { --outer-local : 2 ; result : --inner (); } @function --inner () returns <number>{ result : calc ( var ( --outer-arg) +var ( --outer-local)); } div{ z-index : --outer ( 1 ); /* 3 */ }
同样,自定义属性也是隐式可用的:
@function --double-z () returns <number>{ result : calc ( var ( --z) *2 ); } div{ --z : 3 ; z-index : --double-z (); /* 6 */ }
但函数参数会“遮蔽”自定义属性, 局部变量则会“遮蔽”两者:
@function --add-a-b-c ( --b, --c) { --c : 300 ; result : calc ( var ( --a) +var ( --b) +var ( --c)); /* 使用调用点自定义属性的 --a, 函数参数的 --b, 以及局部变量的 --c */ } div{ --a : 1 ; --b : 2 ; --c : 3 ; z-index : --add-a-b-c ( 20 , 30 ); /* 321 */ }
3. 使用自定义函数
类似于自定义属性的值 可以通过 var() 替换到其他属性值中一样, 自定义函数的求值结果 也可以通过 <dashed-function> 替换到属性值中。
<dashed-function> 是一种函数式表示法,其函数名以两个连字符(U+002D HYPHEN-MINUS)开头。 它的参数语法为:
<dashed-function> = --*( <declaration-value>#? )
<dashed-function> 只能在允许 var() 的地方使用。
如果一个属性包含一个或多个 <dashed-function>, 那么整个属性的语法在解析时应被视为有效。 在计算值阶段, 每个 <dashed-function> 必须先被替换,然后再与属性的语法进行校验。
注意: 在自定义函数体内,var() 可能的解析结果 与 <dashed-function> 用于元素上的解析结果不同。 详见 § 3.1 自定义函数的求值。
<dashed-function> 会在某种上下文中被求值: 可以是在元素上的属性值 (或最终视为元素属性的描述符, 如 @keyframes), 也可以是在另一个自定义函数体中的描述符上, 此时作用于一个“假想”元素。 无论哪种情况都会形成一个调用上下文, 其中包含包含 <dashed-function> 的属性或描述符名称, 以及该属性/描述符要应用到的元素(或“假想”元素)。
当调用上下文被<dashed-function> 在 自定义函数内部 嵌套求值时, 调用上下文的根元素 就是 调用上下文 栈中的真实元素。
-
令 function 为通过 dashed function 的名称作为树作用域引用得到的结果。 如果没有该名称,则返回保证无效值。
-
对 arguments 中的每个 arg,替换任意替换函数, 并用结果替换 arg。
-
如果 dashed function 正在被替换到元素上的属性中, 则令 calling context 为包含该元素和属性的调用上下文
否则,若被替换到“假想元素”的描述符上, 且在求值另一个自定义函数时, 则 calling context 为包含该“假想元素”和描述符的调用上下文。
-
对自定义函数求值, 使用 function、arguments 和 calling context, 并返回求值结果的等效标记序列。
{}
将带逗号的值整体作为单个参数传递:
@function --max-plus-x ( --list, --x) { result : calc ( max ( var ( --list)) +var ( --x)); } div{ width : --max-plus-x ({ 1 px , 7 px , 2 px }, 3 px ); /* 10px */ }
--foo ()
自身与自身形成了循环:
@function --foo ( --x) { result : --foo ( 10 ); }
同样,
也与自身形成了循环,
即使局部变量 --x
并未被
result 调用:
@function --bar () { --x : --bar (); result : 1 ; }
但下例中,
并未与自身循环,
因为我们从未在
规则内求值 result
声明:
@function --baz ( --x) { @media ( unknown-feature) { result : --baz ( 42 ); } result:1 ; }
--baz ()
并未形成循环:
尽管 var ( --x)
和 var ( --y)
出现在函数体中,
它们分别指向函数参数和局部变量。
自定义属性 --x
和 --y
都引用了 --baz ()
,但这没关系:
这些 自定义属性 并未在 --baz ()
内部被引用。
@function --baz ( --x) { --y : 10 px ; result : calc ( var ( --x) +var ( --y)); } div{ --x : --baz ( 1 px ); --y : --baz ( 2 px ); width : var ( --x); /* 11px */ height:var ( --y); /* 12px */ }
3.1. 自定义函数的求值
自定义函数的求值本质上是, 假设其函数体是应用于一个假想元素的样式规则, 按常规解析样式, 然后返回该假想元素上的 result 描述符的值。 这个假想元素“继承”了所有自定义属性的值, 就像它是其调用上下文的子元素一样, 但其函数参数会覆盖同名的“继承”自定义属性。
-
令 substitution context 为一个包含 «"function", custom function» 的替换上下文。
注意: 由于树作用域, 相同的函数名可能多次出现在栈上, 但指向不同的自定义函数。 因此,自定义函数本身会被包含在替换上下文中,而不仅仅是其名称。
-
对 substitution context 进行保护,直至本算法结束。 如果 substitution context 被标记为循环, 则返回保证无效值。
-
令 registrations 为一个初始为空的自定义属性注册表集合。
-
对于 custom function 的每个函数参数, 创建一个以参数名为名、参数类型为语法、inherit 标志为 true、无初始值的自定义属性注册表, 并将注册项加入 registrations。
-
如果 custom function 有返回类型, 创建一个名为 "return"(违反常规注册名规则)、语法为 返回类型、inherit 标志为 false、无初始值的自定义属性注册表, 并将注册项加入 registrations。
-
令 argument rule 为一个初始为空的样式规则。
-
对于 custom function 的每个函数参数:
-
使用 custom function、argument styles、registrations 和 calling context 解析函数样式。 令 argument styles 为结果。
-
对于 registrations 中的每个自定义属性注册表, 设置其初始值为 argument styles 中的对应值, 设置语法为通用语法定义, 并将包含该属性名和属性值的自定义属性前置到 body rule。
-
使用 custom function、body rule、registrations 和 calling context 解析函数样式。 令 body styles 为结果。
-
如果 substitution context 被标记为循环替换上下文, 返回保证无效值。
注意: 嵌套的任意替换函数可能会在步骤2之后某处将 substitution context 标记为循环, 例如在解析 result 时。
-
返回 body styles 中 result 属性的值。
-
创建一个“假想元素” el,其作为 calling context 的元素的子元素。el 是无特征的, 只适用自定义属性和 result 描述符。
-
将 rule 应用于 el 的指定值阶段, 并进行如下修改:
-
对于自定义属性, CSS 通用关键字 initial 和 inherit 按常规生效; 其它 CSS 通用关键字 均解析为保证无效值。
注意: initial 会引用由函数参数创建的自定义属性注册表, 允许你“重置”属性为传入值。inherit 继承自调用上下文的元素。\
注意: 例如 result: inherit, 会导致 <dashed-function> 求值为 inherit 关键字, 类似 var(--unknown, inherit)。
-
对于某个自定义属性 prop, 在对该属性进行属性替换时, 替换上下文 也包含 custom function。 换句话说,替换上下文 是 «"property", prop 的名称, custom function»
注意: 由于动态作用域, 相同属性名可多次出现在栈上, 但指向不同的自定义属性。 因此自定义函数本身被包含在替换上下文中,而不仅仅是其名称。
-
计算 el 上所有自定义属性及 result “属性”的计算值, 详见 CSS Properties and Values API 1 § 2.4 计算值阶段行为, 并结合上一步的变更, 以及如下内容:
-
除对自定义属性的引用(正常使用 el 上的值) 和数字/百分比(在自定义属性中保持未解析)外, 所有通常会引用被样式化元素的值 现在都引用 calling context 的根元素。
注意: 例如属性中的 attr(), 或规则中的 @container 查询。
-
-
返回 el 的样式。
4. 自定义函数的执行模型
与 CSS 其他部分一样,自定义函数遵循声明式模型。
局部变量描述符 和 result 描述符 可以以任意顺序出现, 并且可以多次出现。 如果发生这种情况,则后出现的声明会覆盖先前的声明。
@function --circle-area ( --r) { result : calc ( pi *var ( --r2)); --r2 : var ( --r) *var ( --r); }
局部变量 描述符可以出现在被引用之前或之后。
4.1. 条件规则
出现在 @function 内的条件分组规则 会变成嵌套分组规则, 但有额外限制, 只有 @function 内允许的描述符才能出现在 嵌套分组规则 内。
条件分组规则 在 @function 内会 正常处理, 相当于在条件为真时将规则内容插入到 条件分组规则 所在位置, 否则该位置视为空。
@function --suitable-font-size () { result : 16 px ; @media ( width >1000 px ) { result : 20 px ; } }
如果媒体查询条件为真,result 描述符的值为
,
否则为
。
@function --suitable-font-size () { @media ( width >1000 px ) { result : 20 px ; } result:16 px ; }
上述例子中,result 描述符的值始终为
。
@function --suitable-font-size () { --size : 16 px ; @media ( width >1000 px ) { --size : 20 px ; } result:var ( --size); }
5. CSSOM
5.1. CSSFunctionRule
接口
CSSFunctionRule
接口表示一个 @function 规则。
[Exposed =Window ]interface :
CSSFunctionRule CSSGroupingRule {readonly attribute CSSOMString name ;sequence <FunctionParameter >();
getParameters readonly attribute CSSOMString returnType ; };
name
,类型为 CSSOMString,只读- 自定义函数的名称。
returnType
,类型为 CSSOMString,只读- 自定义函数的返回类型,
以语法字符串形式表示。
如果 自定义函数没有返回类型,
则返回
。"type(*)"
dictionary {
FunctionParameter required CSSOMString ;
name required CSSOMString ;
type CSSOMString ?; };
defaultValue
- name
- 函数参数的名称。
- type
- 参数类型,
以语法字符串表示,
如果 参数没有类型,则为
。"type(*)" - defaultValue
- 函数参数的默认值, 如果该参数没有默认值则为 `null`。
虽然声明可以直接在 @function 规则中指定,
但在 CSSOM 中不会以这种方式表示。
相反,连续的声明片段
会如同被包裹在 CSSFunctionDeclarations
规则中一样出现。
注意: 这同样适用于 @function 规则中的“前置”声明, 即那些没有跟在其他嵌套规则后面的声明。
@function --bar () { --x : 42 ; result : var ( --y); @media ( width >1000 px ) { /* ... */ } --y:var ( --x); }
上述内容在 CSSOM 中表现为:
@function --bar () { /* CSSFunctionDeclarations { */ --x:42 ; result : var ( --y); /* } */ @media ( width >1000 px ) { /* ... */ } /* CSSFunctionDeclarations { */ --y:var ( --x); /* } */ }
-
字符串
,后跟一个空格(U+0020)。"@function" -
一个右括号(U+0029)。
-
如果 自定义函数有返回类型, 且该 返回类型 不是 通用语法定义("*"):
-
一个空格(U+0020), 后跟字符串
, 再后跟一个空格(U+0020)。"returns" -
对该类型执行 序列化 CSS 类型, 后跟一个空格(U+0020)。
-
-
一个左花括号(U+007B),后跟一个空格(U+0020)。
-
对 cssRules 中的每条规则执行序列化 CSS 规则, 过滤掉空字符串, 用一个空格(U+0020)连接所有结果。
注意: 序列化 CSS 规则 在序列化空的
CSSFunctionDeclarations
规则时可以返回空字符串。 -
一个空格(U+0020),后跟一个右花括号(U+007D)。
-
如果 <css-type> 由单个 <syntax-component> 组成, 则返回对应的语法字符串。
-
否则, 返回以下内容的拼接:
-
字符串
, 即"type("
后跟一个左括号(U+0028)。"type" -
对应的语法字符串。
-
字符串
, 即一个右括号(U+0029)。")"
-
5.2.
CSSFunctionDeclarations
接口
CSSFunctionDeclarations
接口表示 声明 在 @function 规则内连续出现的一段。
[Exposed =Window ]interface :
CSSFunctionDescriptors CSSStyleDeclaration {attribute [LegacyNullToEmptyString ]CSSOMString ; }; [
result Exposed =Window ]interface :
CSSFunctionDeclarations CSSRule { [SameObject ,PutForwards =cssText ]readonly attribute CSSFunctionDescriptors style ; };
style
属性
必须为该规则返回一个 CSSFunctionDescriptors
对象,
包含以下属性:
- computed flag
-
未设置
- readonly flag
-
未设置
- declarations
- parent CSS rule
- owner node
-
Null
CSSFunctionDeclarations
规则与 CSSNestedDeclarations
类似,序列化时表现为其声明块被直接序列化一样。
6. 隐私注意事项
本规范定义的结构完全在 CSS 内部定义和使用; 不会暴露任何新信息。
7. 安全注意事项
目前没有针对本规范提出安全相关问题。