【问题标题】:Besides CSS variables, what are CSS custom properties used for?除了 CSS 变量,CSS 自定义属性还有什么用途?
【发布时间】:2018-08-26 02:12:55
【问题描述】:

来自 CSS 工作组 post

自定义属性不仅仅用于变量 [...]

与很多人的想法相矛盾(请参阅this),这篇文章不仅证实了变量和自定义属性不是一回事,而且自定义属性可以用于变量以外的其他事物。

我的问题是关于最后一部分。有没有人遇到过一些 CSS 代码,其中自定义属性用于变量以外的东西?

【问题讨论】:

标签: css css-variables


【解决方案1】:

您链接的Stack Overflow answer 实际上没有错误,并且正在谈论与您所要求的内容略有不同的东西。它讨论了在相关的 CSS 规范中,自定义属性通常如何被描述为易于理解的变量,尽管它们的工作方式与其他语言中的变量的工作方式不同(但这与 CSS 课程类似,是不是吗?)。

CSS 自定义属性的可变部分实际上在于您如何调用(或使用)自定义属性:

.example {
    background: var(--custom-property);
}

正如您在the spec 中看到的那样,全标题为“级联变量模块级别 1 的 CSS 自定义属性”,只有一个用途:被称为具有 var() 的级联元素中的值。因此,当该 wiki 帖子的作者说自定义属性不仅仅用于变量时,这有点不清楚是什么意思。此外,后续句子并没有真正描述或支持该声明,他们只是谈论使用$ 而不是- 是如何“奇怪”的。

也许他们的意思是你可以声明一个只有在被 JavaScript 读取时才有效的自定义属性?或者在 JS 中声明一个自定义属性,然后将其应用于 CSS。自定义变量语法支持 CSS 解析器无法正确读取但在 JavaScript 中可以正确读取的方程。但是,结果仍然是使用var() 声明的元素的属性值

从某种角度来看,如果它们在属性中用于 以外的东西,则称它们为自定义属性 是没有意义的。该规范没有提到在 CSS 或 JS 中调用或使用属性的替代方法,而不在级联选择器的属性值部分({} 之间的代码位)中使用 var()

【讨论】:

  • 现在,已经回答了这个问题......我已经联系了该维基帖子的原作者,看看他们是否可以在这里提供一些澄清(或反驳)。
  • 经过更多探索后,我在工作组的 Wiki 草案中找到了this(参见注释 2,第 2 节)。注释指出“注意:虽然此模块侧重于使用带有 var() 函数的自定义属性来创建“变量”,但它们也可以用作实际的自定义属性,由脚本解析和执行。预计CSS 扩展规范 [CSS-EXTENSIONS] 将扩展这些用例并使它们更易于操作。”
  • @MahboubiSalim 是的,这与我关于使用自定义属性的 JavaScript 的倒数第二段不谋而合。只是 JavaScript 进行声明而不是 CSS(并且也在当前/已发布的规范版本中)
  • 哦!没看到编辑。提到的草案链接到this one 它指出:“需要更全面地支持自定义属性(并最终将它们从变量规范中完全删除,因为它们将在此处定义)。”所以这是一项正在进行的工作,我们将不得不等待,看看自定义属性是否会在没有var() 的情况下真正使用
  • @MahboubiSalim 如果 Tab 能够提供一些说明,那将非常有用。他是唯一有资格做出回应的人,因为他是引用和 wiki 页面的作者,也是规范的主要作者。同时,希望这个答案(以及其他答案,两者都很好)有助于澄清一些。
【解决方案2】:

请注意,您引用的文本的上下文是 CSSWG 解释为什么使用 --foovar(--foo) 代替 $foo。我的猜测是,他们所说的“自定义属性不仅仅用于变量”是指--foo 标识符语法设置为出现在其他地方,用于其他自定义事物,例如自定义媒体查询最初在 MQ4 中,现在在 MQ5 中。显然,这些不是 级联 变量,因为媒体查询不会级联 (their enclosed rules do)。 (虽然,如果是这样的话,“自定义属性”应该是别的东西,但 CSSWG 可能还没有为这些东西考虑一个合适的集体名称。)

有没有人遇到过一些 CSS 代码,其中自定义属性用于变量以外的东西?

我没有遇到任何真实世界的示例,但我只是敲了一个示例,它使用 JavaScript 根据自定义属性切换元素上的类,这反过来会影响 CSS 中其他地方的选择器:

let p = document.querySelector('p');

if (window.getComputedStyle(document.documentElement).getPropertyValue('--boolean').trim() === "'true'")
  p.classList.add('toggle');
else
  p.classList.remove('toggle');
:root {
  --boolean: 'true';
}

p.toggle {
  color: #f00;
}

p::before {
  content: 'Value of --boolean is "' var(--boolean) '"';
}
<p>

当然,需要注意的是,这是 CSS 和 JavaScript。除了 CSS 中的级联变量之外,您不能将自定义属性用于任何其他内容,因为 custom properties participate in the cascade 与任何其他 CSS 属性非常相似,并且不存在于它之外。这就是authors conflate the two terms in everyday usage 的原因——因为它们之间的任何差异都是学术性的。想要依赖自定义属性的样式表的任何其他部分都必须使用脚本才能使用它。

您可能想知道为什么这样的东西不能在 CSS 中实现。考虑:

p.toggle {
  --boolean: 'false';
}

如果我们要提出一个伪类来根据元素的 CSS 属性值匹配元素,例如 p:prop(--boolean: 'true')

  • 在 CSS 之外,伪类应该有效且始终匹配、有效且永不匹配还是无效?
  • 反射值应该是多少?指定值、计算值或使用值?请注意,由于历史原因,即使 window.getComputedStyle() 也并不总是返回计算值。
  • 循环依赖变得不可避免:

    :root {
      --boolean: 'true';
    }
    
    /* 
     * Causes p to stop matching the selector, switching back to
     * inheriting from :root, which in turn causes it to start
     * matching the selector again, ad infinitum.
     */
    p:prop(--boolean: 'true') {
      --boolean: 'false';
    }
    

仔细查看上面的 JavaScript 示例,您会发现它并不健壮。文档完成后,属性值只评估一次。再次更改自定义属性值,元素不会更新自身以反映更改。如果它监听到自定义属性值的变化,它会立即遇到相同的循环依赖问题——更糟糕的是,锁定你的浏览器(直到它注意到并拦截无限递归)。

【讨论】:

    【解决方案3】:

    其他问题解释了自定义属性和 CSS 变量,但是如果我们简单地说自定义属性只是为了让我们在 CSS 默认属性之外添加更多属性,那会怎样。当然,单独的自定义属性是没有用的,因为浏览器不会对它做任何事情,这就是为什么在 99% 的情况下它们与 CSS 变量一起使用。

    另一个事实是,在做 CSS 时,我们会直观地考虑使用默认的 CSS 属性来做我们想做的事情,当我们面临复杂的情况时,我们会尝试将很多属性、伪元素等组合在为了满足我们的需求。但是,如果我们以不同的方式思考并开始为此目的使用自定义属性呢?如果不是复杂的 CSS 代码,我们只需编写一个自定义属性并实现一个 JS/jQuery 代码来为我们完成这项工作。


    让我们举一个简单而常见的例子。您有一个元素,您想将其设置为绝对位置,以便将其用作其父元素的叠加层。使用 CSS,我们将执行以下操作:

    .block {
      height:100px;
      width:100px;
      margin:20px; 
      border:1px solid red;
      position:relative; /* we usually forget this !*/
    }
    
    .overlay {
      position:absolute;
      top:0;
      right:0;
      left:0; /*sometimes we use width:100% but it won't work with padding!*/
      bottom:0;
      padding:5px;
      z-index:1; /* we can forget this sometimes*/
      background:rgba(0,0,0,0.5);
    }
    <div class="block">
    <span class="overlay"></span>
      some text here
    </div>

    这很简单,如果我们想拥有相同的东西,我们可以在任何地方使用相同的类。但是我们可以考虑使用自定义属性来减少 CSS 代码并使其更容易:

    $('*').each(function() {
      if ($.trim($(this).css('--overlay')) === "top") {
        $(this).css({
          'position': 'absolute',
          'top': 0,
          'bottom': 0,
          'left': 0,
          'right': 0,
          'z-index':2
        });
        $(this).parent().css('position','relative');
      } else {
        //we test the other values and we do the necessary changes
      }
    })
    .block {
      height: 100px;
      width: 100px;
      margin: 20px;
      border: 1px solid red;
    }
    
    .overlay {
      --overlay: top;
      background: rgba(0, 0, 0, 0.5);
    }
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <div class="block">
      <span class="overlay"></span> 
      some text here
    </div>

    我知道第一个想法是“为什么要费心使用所有这些代码来完成我们可以使用 CSS 完成的如此简单的事情?”和“如果属性发生变化怎么办?如果父级已经设置了位置怎么办?”这是真的,但这是一个非常简单的例子。想象一下,您构建了一个 JS 或 jQuery 库,在其中为复杂的东西提供了许多自定义属性(创建常见的 CSS 形状,在难以计算值的情况下进行复杂的转换等),并为每个属性提供了一个文档及其各自的值。

    它就像一个 JS/jQuery 库,它告诉您向元素添加一个类并调用一个函数,然后您就有一个响应式滑块、一个交互式地图、一个 twitter 小部件等。为什么不对自定义属性做同样的事情?我们要求人们包含该库并简单地编写 CSS,然后看看它的魔力。


    我们也可以通过添加更多有用的属性来将其视为升级,以使事情变得更容易。就像 CSS 制造商所做的那样,但我们是自己做的,不要等到有新的规范出现后再批准。

    我们以 flexbox 为例。在 flexbox 之前,如果我们想在一个大小完全相同的容器中拥有 N 个 div 并填充所有容器,我们必须编写一些复杂的代码,每次添加新元素时都必须调整这些代码。现在有了 flexbox,我们只需设置 flex:1 就可以了!浏览器将为我们完成所有复杂的工作。

    我们可以做同样的事情。我们创建自定义属性以使事情变得更容易,并将其分享给社区。人们将开始使用它们并可能会发现它们很有用,然后它们可以成为常见问题的参考。也许某些浏览器甚至会考虑一种实现来集成它们。

    所以自定义属性也可以成为增强 CSS 的一种方式。

    【讨论】:

    • 换句话说,它在规模上很有用...... 20 行的 CSS 文件不值得使用自定义属性。但成百上千,它开始看起来非常有用。
    • @TylerH 是的,这是主要目的之一,另一个是新学习者可以很容易地编写 CSS 而无需了解所有内容......他们可以创建一个覆盖并不要'不需要知道为什么父母需要是相对的,为什么使用上/右/左/下来拉伸以及什么是z-index等......当然这不是一个很好的例子,因为知道这些很好东西,但对于更复杂的 CSS,它可以用来实现一些东西,而无需深入了解它是如何工作的
    • @TylerH 我编辑了我的 anwser 以包含另一个可能有用的用例 ;)
    猜你喜欢
    • 2019-04-12
    • 1970-01-01
    • 2011-01-26
    • 2018-11-12
    • 2018-07-30
    • 2018-05-29
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    相关资源
    最近更新 更多