0

Is there a native API which can update the CSS variables scoped under a particular CSS class(or even any other complex CSS selector)predefined in a stylesheet? The question can be generalized for not just CSS variables but other CSS properties as well, i.e whether class specific CSS properties can be updated without targeting a specific HTML element, but by targeting the class definition itself.

Please find below the code snippets which demonstrates an example scenario. You can also find comments in the code to as to what I believe is happening/I am doing on specific lines.

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>

Please try running this to get a full idea of intended effect. You can click "Change Color" button at the bottom to see the effects in action.

To get the intended overriding for CSS variable --bg-color for classes inner-primary and inner-secondary, I had to use querySelectorAll with the required CSS selector(in this case just a class name) and iteratively set the CSS variable for each individual element found.

By nature of how CSS gets read by the browser, feels like the other solution to this is to dynamically insert a style element tag into the DOM, with the required CSS variable update, scoped under the required class name(or any other required selector).. But this feels awkward and can quickly get out of hand if we don't implement some system to reuse the same style tag and not insert new ones during each toggle.

Is there any other way to do this? Any native API which can solve this without having to access individual elements or without inserting style tags dynamically..?

Yadu S
  • 19
  • 2
  • 1
    Have you tried changing class settings dynamically? See for example the 2019 answer from @KamilKiełczewski at [link]https://stackoverflow.com/questions/5753680/change-css-of-class-in-javascript – A Haworth Jun 22 '21 at 19:58
  • @AHaworth thanks for the input . I was able to update the code using CSSStyleSheets. You can find my answer below.. – Yadu S Jun 23 '21 at 12:16

1 Answers1

0

As suggested by A Haworth and referring Change CSS of class in Javascript? I was able update changeColor function to use CSSStyleSheet(MDN link) instead. Please find the updated function below, which uses this 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;
}

This is still some concern here since it seems like we can only overwrite the entire cssRule(which may also include other CSS properties) for a particular selector, and not just one required property. But this may arguably be better than updating each individual element style or inserting style tags as mentioned in the question.

Can check the full working codepen at => https://codepen.io/yadus/pen/mdWZmXX

Yadu S
  • 19
  • 2