Home > Mobile >  How do you get/set non root CSS variables from JavaScript
How do you get/set non root CSS variables from JavaScript

Time:07-04

Lots of answers on how to get/set "root" CSS variables but none that I've found on how to get/set NON root CSS variables.

NOTE: This is NOT answer: https://stackoverflow.com/a/36088890/104380 That answer only handles root variables and or variables on elements, not on classes

Also: These are not answers (from: https://www.google.com/search?q=get/set css variables). Checked the first page of links. All of them only handle root variables. I'm trying to deal with non-root variables.

.foo {
  --fg-color: red;
}
.bar {
  --fg-color: blue;
}
div {
 color: var(--fg-color);
}
<div >foo</div>
<div >foo</div>

How do you get someFunctionToGetCSSVariable('.foo', '--fg-color') (yes, I made that up). The point is I want to get/set the --fg-color variable on the .foo CSS rule. Whatever the solution is, getting the value it should return 'red' or '#FF0000') and how do you set it to something else?

CodePudding user response:

I think what OP tries to do is not to change the all elements of that certain class, but to change the class itself, there is a difference. What you need (if I understood well) is actually cssRules change on the certain class. I've created a quick (and dirty) snippet of how to search the cssRules and change the CSS properties in it (including custom one):

<html>
<head>
<style>
.foo {
  --fg-color: red;
}
</style>
</head>
<body>
<div >lorem ipsum</div>



<script>
window.onload = function() {
  rules = document.styleSheets[0].cssRules
  for (var r in rules) {
      if (rules[r].selectorText == ".foo") {
        rules[r].style.setProperty('--fg-color', 'blue');
        alert('hi');
        break;
      }
  }
};
</script>
</body>
</html>

CodePudding user response:

You could make use of the fact that we have cascading style sheets.

It depends on exactly what your structure is of course, (e.g. are there style elements buried in body?).

For the simple case you could add another stylesheet onto the bottom of the head element.

<!doctype html>
<html>
<head>
<style>
.foo {
  --fg-color: red;
}
.bar {
  --fg-color: blue;
}
div {
 color: var(--fg-color);
}
</style>
</head>
<body>
<div >foo</div>
<div >foo</div>
<button>Click me</button>
<script>
const button = document.querySelector('button');
button.addEventListener('click', function () {
  const head = document.querySelector('head');
  const style = document.createElement('style');
  style.innerHTML = '.foo { --fg-color: lime; }';
  head.appendChild(style);
  button.style.display = 'none'; 
});
</script>

</body>
</html>

Of course, it could get messy if this happens more than once. You'd want to remember that you'd added a style sheet and get rid of it before adding another one, or alter it in situ, depending on exactly what the situation is. And if there is styling scattered in the body you'll have to add the stylesheet to the bottom of that.

CodePudding user response:

Looks like I have to iterate through all the stylesheets and find the rule. Here's one try.

function getCSSRuleBySelector(selector) {
  for (let s = 0; s < document.styleSheets.length;   s) {
    const rules = document.styleSheets[s].cssRules;
    for (let i = 0; i < rules.length;   i) {
      const r = rules[i];
      if (r.selectorText === selector) {
        return r;
      }
    }
  }
}

function setCSSVariableBySelector(selector, varName, value) {
  const rule = getCSSRuleBySelector(selector);
  rule.style.setProperty(varName, value);
}

function getCSSVariableBySelector(selector, varName) {
  const rule = getCSSRuleBySelector(selector);
  return rule.style.getPropertyValue(varName);
}

console.log('was:', getCSSVariableBySelector('.foo', '--fg-color'));
setCSSVariableBySelector('.foo', '--fg-color', 'green');
.foo {
  --fg-color: red;
}
.bar {
  --fg-color: blue;
}
div {
  color: var(--fg-color);
}
<div >foo</div>
<div >foo</div>
<div >foo</div>
<div >bar</div>
<div >bar</div>

CodePudding user response:

You can use document's styleSheets property to access the CSS rules of the linked .css file, and perform some operations on it to get the value of the custom css variable.
To set a value of custom CSS variable (of the class itself), you can create a new style element, and add a rule for it.

function getStyle(selector, prop) {
  let rules = getRule(selector).cssText;
  return rules.split(prop   ":")[1].replace(";", "").replace("}", "").trim();
}

function setStyle(selector, prop, val) {
  let style = document.querySelector("style.customStyle");
  if(style === null){
    style = document.createElement("style");
    style.className = "customStyle";
    style.innerHTML = `${selector}{${prop}:${val}}`;
    document.head.appendChild(style);
  } else{
    style.innerHTML  = `${selector}{${prop}:${val}}`;
  }
}

function getRule(selector) {
  let rulesObj = document.styleSheets[0];
  let classes =  rulesObj.rules || rulesObj.cssRules;
  classes = Object.values(classes)
  let rules = classes.filter(c => c.selectorText === selector)[0];
  return rules;
}

console.log(getStyle(".foo", "--fg-color"));
console.log(getStyle(".bar", "--fg-color"));

document.querySelector("button").onclick = ()=>{
  setStyle(".bar", "--fg-color", "green");
}
.foo {
  --fg-color: red;
}

.bar {
  --fg-color: blue;
}

div {
  color: var(--fg-color);
}
<div >foo</div>
<div >foo</div>
<div><button>Click</button></div>

CodePudding user response:

Get

Iterate all styles

Styles can be injected via external CSS file, a <style> tag (on the page) or style attribute (irrelevant to you).

const results = [];
const SELECTOR = '.foo';
const PROP = '--bar';

[...document.styleSheets].forEach(sheet => {
  [...sheet?.cssRules].forEach(rule => {
    // you should probably use regex to find PROP (so it won't match `--bar-x` for example)
    if( rule.cssText.includes(SELECTOR) && rule.cssText.includes(PROP) ){
      results.push(rule.cssText)
    }
  })
});

console.log(results)
.foo { --bar: red }

Now you need to parse the result and extract the property value. You did not specify in your question if CSS selectors' specificity should be regarded, which makes things a lot more complex.

There might also be selectors which implicitly applies style properties to elements. Examples of different specificities:

/* assuming '.foo' is a div child of <body> */
.foo { --x:1 }  
body .foo { --x:2 }  
body > div { --x:3 }
body * { --x:4 }
div { --x:5 }

Set

Regarding setting, you need to dynamically add a rule or a style tag:

document.head.insertAdjacentHTML("beforeend", `<style id="whatever">--fg-color: red;</style>`)

And when you want to update it, just overwrite the #whatever <style> with innerHTML.

CodePudding user response:

To set the value you can use document.querySelectorAll and the setProperty method on the style of each object:

function setCssVariable(selector, variable, value) {
    document.querySelectorAll(selector).forEach(function (element) {
        element.style.setProperty(variable, value);
    });
}

setCssVariable(".foo", "--fg-color", "black");

And to get the value, assuming all elements of the class have the same value, you can use a similar approach using document.querySelector and the getProperty method on style of the element:

function getCssVariable(selector, variable) {
    const element = document.querySelector(selector);
    if (element !== null) {
        return element.style.getPropertyValue(variable);
    }
}

getCssVariable(".foo", "--fg-color");

CodePudding user response:

You can read the variable value from the getComputedStyle method of JS and set the variable value with style attribute.

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

/*You can get the value of variable property from the element where you used the variable*/
//console.log(getComputedStyle(foo).getPropertyValue('--fg-color'));

/*You can set the variable value like this for every element where used the variable and want to choose at runtime with js */
//foo.style.cssText = '--fg-color:black;';



function setCSSProperty(cls, prop, val){
  
  let els = document.querySelectorAll(cls);
  if(els){
    els.forEach(el => {
      el.style.cssText  = `${prop}:${val};`
    })
  }

}

function getCSSPropertyData(cls, prop){
  
  let els = document.querySelectorAll(cls);
  let data = [];
  if(els){
    els.forEach((el,ind) => {
      let cs = getComputedStyle(el);
      data[ind] = cs.getPropertyValue(prop).trim();
      
    });
  }
  return data;
}

console.log(getCSSPropertyData('.foo', '--fg-color'));


setCSSProperty('.foo', '--fg-color', 'green');
.foo {
  --fg-color: red;
}
.bar {
  --fg-color: blue;
}
div {
 color: var(--fg-color);
}
<div >foo</div>
<div >foo</div>

  • Related