首页 > 解决方案 > 如何通过 JavaScript 添加/更新作用域 CSS 变量

问题描述

是否有本机 API 可以更新样式表中预定义的特定 CSS 类(甚至任何其他复杂的 CSS 选择器)下的 CSS 变量?这个问题不仅可以推广到 CSS 变量,还可以推广到其他 CSS 属性,即是否可以在不针对特定 HTML 元素的情况下更新类特定的 CSS 属性,而是针对类定义本身。

请在下面找到演示示例场景的代码片段。您还可以在代码中找到关于我认为正在发生的事情/我在特定行上所做的注释。

var toggle = true;
function changeColor() {
  document.documentElement.style.setProperty('--bg-color', toggle ? 'green' : 'red');
  // this works for the "outer" div since there we receive global value(value defined in :root) of --bg-color
  toggle = !toggle;

  // here I want to also change the scoped value of --bg-color for "inner-primary" and "inner-secondary"
  // currently I can do this by doing:
  document.querySelectorAll('.inner-primary').forEach(ele => ele.style.setProperty('--bg-color', toggle ? 'blue' : 'yellow'))
  document.querySelectorAll('.inner-secondary').forEach(ele => ele.style.setProperty('--bg-color', toggle ? 'yellow' : 'blue' ))

  // another way I can see is: we dynamically insert a style tag, but this feels very awkward and can quickly get out of hand on multiple iterations
}
:root {
  --bg-color: red;
}
body {
  margin: 0;
}
.outer {
  width: 100vw;
  height: auto;
  min-height: 100vh;
  text-align: center;
  background-color: var(--bg-color); /* receives value from :root */
}
.inner-primary,
.inner-secondary {
  width: 100px;
  height: 100px;
  /* received scoped value from .inner-primary or .inner-secondary defined below*/
  background-color: var(--bg-color);
  border: 1px solid black;
  margin: auto;
  margin-bottom: 10px;
}
.inner-secondary {
  --bg-color: yellow;
}
.inner-primary {
  --bg-color: blue;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Testing</title>
  </head>
  <body>
    <div class="outer">
      <div class="inner-primary"></div>
      <div class="inner-secondary"></div>
      <div class="inner-primary"></div>
      <div class="inner-secondary"></div>
      <button onclick="changeColor()">Change Color</button>
  </body>
</html>

请尝试运行它以全面了解预期效果。您可以单击底部的“更改颜色”按钮以查看实际效果。

要为类inner-primaryinner-secondary获得 CSS 变量--bg-color的预期覆盖,我必须将querySelectorAll与所需的 CSS 选择器(在本例中只是一个类名)一起使用,并为每个类迭代设置 CSS 变量找到单个元素。

根据浏览器如何读取 CSS 的性质,感觉另一种解决方案是将样式元素标签动态插入 DOM,并使用所需的 CSS 变量更新,范围在所需的类名(或任何其他所需的选择器) .. 但是如果我们不实现一些系统来重用相同的样式标签并且在每次切换期间不插入新的标签,这感觉很尴尬并且很快就会失控。

有没有其他方法可以做到这一点?任何本机 API 都可以解决这个问题,而无需访问单个元素或动态插入样式标签..?

标签: javascripthtmlcssdomcss-variables

解决方案


正如A Haworth所建议的那样,并提到Change CSS of class in Javascript? 我能够更新 changeColor 函数以改用CSSStyleSheet(MDN 链接)。请在下面找到使用此 API 的更新函数:

var toggle = true;
function changeColor() {
  document.documentElement.style.setProperty('--bg-color', toggle ? 'green' : 'red');

  // solution using document stylesheets
  const styleSheet = document.styleSheets[0]
  const cssRules = Array.from(styleSheet.cssRules);
  const primaryClassIndex = cssRules.findIndex(cssRule => cssRule.selectorText === '.inner-primary');
  const secondaryClassIndex = cssRules.findIndex(cssRule => cssRule.selectorText === '.inner-secondary');

  //update primary:
  styleSheet.deleteRule(primaryClassIndex);
  styleSheet.insertRule(`.inner-primary {--bg-color: ${toggle ? 'yellow' : 'blue'};}`, primaryClassIndex)

  //update secondary:
  styleSheet.deleteRule(secondaryClassIndex);
  styleSheet.insertRule(`.inner-secondary {--bg-color: ${toggle ? 'blue' : 'yellow'};}`, secondaryClassIndex)
  
  //toggle
  toggle = !toggle;
}

这仍然是一些问题,因为我们似乎只能覆盖特定选择器的整个cssRule(可能还包括其他 CSS 属性),而不仅仅是一个必需的属性。但这可以说比更新每个单独的元素样式或插入问题中提到的样式标签更好。

可以在 => https://codepen.io/yadus/pen/mdWZmXX查看完整的工作代码笔


推荐阅读