1

Similar question : But can't able to get answer

Can be answer to this question : But has to split on each :

If possible to get all variable in one function and change each values

If there are 2-4 variable than easy to change but when that change to 10 or more

var root = document.querySelector(':root');

function func() {
  root.style.setProperty('--r', 'brown');
  root.style.setProperty('--b', 'lightblue');
  root.style.setProperty('--g', 'lightgreen');
}
:root {
  --r: red;
  --b: blue;
  --g: green;
}

.text1 {
  color: var(--r)
}

.text2 {
  color: var(--b)
}

.text3 {
  color: var(--g)
}
<div class="text1">Hello</div>
<div class="text2">Bye</div>
<div class="text3">World</div>
<button onclick="func()">Change</button>

Is there a way to automatic(dynamically) get all the variables without writing each variable while values array is self entered

var root = document.querySelector(':root');
var roots = getComputedStyle(root);
var re = roots.getPropertyValue('--r')
var bl = roots.getPropertyValue('--b')
var gr = roots.getPropertyValue('--g')


function func() {
  root.style.setProperty('--r', 'brown');
  root.style.setProperty('--b', 'lightblue');
  root.style.setProperty('--g', 'lightgreen');
}

function func2() {
  root.style.setProperty('--r', re);
  root.style.setProperty('--b', bl);
  root.style.setProperty('--g', gr);
}
:root {
  --r: red;
  --b: blue;
  --g: green;
}

.text1 {
  color: var(--r)
}

.text2 {
  color: var(--b)
}

.text3 {
  color: var(--g)
}
<div class="text1">Hello</div>
<div class="text2">Bye</div>
<div class="text3">World</div>
<button onclick="func()">Change</button>
<button onclick="func2()">Orignal</button>

But when getting back to original values then each value is entered by separate variable and for each it is defined .
Is there a way to have a approach which takes original values and variables in separate array automatically.

Thanks for help in advance

Rana
  • 2,500
  • 2
  • 7
  • 28

5 Answers5

3

I understand you want to first read all the declared variables from the css and store them, so they might be resetted after applying new values? This code will do this, given that there's just one ':root' declaration in one stylesheet (easily complemented in case 'splitted' declaration in more places needed)

let variableLookup = {
  '--r': {
    newValue: 'teal'
  },
  '--b': {
    newValue: 'orange'
  },
  '--g': {
    newValue: 'purple'
  }
}

var root = document.querySelector(':root');

function setNewColors() {

  const cssText = [...document.styleSheets]
    .map(styleSheet => [...styleSheet.cssRules]
      .filter(CSSStyleRule => CSSStyleRule.selectorText === ':root'))
    .flat()[0].cssText

  // cssText = ':root { --r: red; --b: blue; --g: green; }'  

  cssText.match(/{(.*)}/)[1].trim()
    .split(';')
    .filter(Boolean)
    .forEach(declaration => {
      const [variable, oldValue] = declaration.split(':').map(str => str.trim())
      let entry = variableLookup[variable]
      if (entry) entry.oldValue = oldValue
    })

  console.log('variableLookup >>', variableLookup)

  Object.entries(variableLookup).forEach(([variable, {newValue}]) => {
    root.style.setProperty(variable, newValue);
  })
}

function resetColors() {
  Object.entries(variableLookup).forEach(([variable, {oldValue}]) => {    
    if (oldValue) root.style.setProperty(variable, oldValue)
  })
}
:root {
  --r: red;
  --b: blue;
  --g: green;
}

.text1 {
  color: var(--r)
}

.text2 {
  color: var(--b)
}

.text3 {
  color: var(--g)
}

:root {
  --c: magenta;
}
<div class="text1">Hello</div>
<div class="text2">Bye</div>
<div class="text3">World</div>
<button onclick="setNewColors()">Change to new colors</button>
<button onclick="resetColors()">Reset old colors</button>

Since the OP is interested in a version without using .split() these could be replaced by using a regex and .match()

const declarations = '--r: red; --b: blue; --g: green;'  

const regex1 = /^[\w-:\s]+(?=;)|(?<=;)[\w-:\s]+/g 

const declarationsArr = declarations.match(regex1)

console.log(declarationsArr)  // ["--r: red", " --b: blue", " --g: green"]

const regex2 = /[\w-]+(?=:)|(?<=:\s)[\w-]+/g

const declarationsSplit = declarationsArr.map(d => d.match(regex2))

console.log(declarationsSplit)  // [["--r", "red"], ["--b", "blue"], ["--g", "green"]]
Corrl
  • 6,206
  • 1
  • 10
  • 36
  • It is possible without the `split` method as I already mentioned a answer with `split` in question. Also can you tell about `filter` and `flat` a little bit – Rana Nov 04 '21 at 07:06
  • What's the reason for `.split()` beeing a problem? The `.filter()` method "creates a new array with all elements that pass the test implemented by the provided function" so in this case it filters the CSSRules where the condition `CSSStyleRule.selectorText === ':root'` is true. The `.flat()` method "creates a new array with all sub-array elements concatenated into it recursively up to the specified depth." So for example `[[1], [2]] => [1,2]` – Corrl Nov 04 '21 at 08:33
  • I recommend to take a look at the MDN docs which I think are a great resource - [.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) & [.flat()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) – Corrl Nov 04 '21 at 08:33
  • @Rana I just noticed there's no question mark, but this "It is possible without the split method as I already mentioned a answer with split in question." is a question, isn't it? An alternative to the first `.split(';')` at the semicolon would be a regex `const regex = /^[a-zA-Z-:\s]*(?=;)|(?<=;)[a-zA-Z-:\s]*/g;` with `.match()` if that was ok to use – Corrl Nov 04 '21 at 09:54
  • I just added the regex versions and would be still curious to know, what's the reason for avoiding .split()? – Corrl Nov 04 '21 at 17:39
  • Thanks for time and effort(+1), I am really glad you answered – Rana Nov 10 '21 at 05:58
1

One method can be entering all variables and values in different arrays and fetching values from them . Where variables = values which need to be equal have same index number

var root = document.querySelector(':root');

variable = ['--r', '--b', '--g'];
values = ['violet', 'lightblue', 'lightgreen']

function func() {
  for (let i = 0; i < 3; i++)
    root.style.setProperty(variable[i], values[i]);
}
:root {
  --r: red;
  --b: blue;
  --g: green;
}

.text1 {
  color: var(--r)
}

.text2 {
  color: var(--b)
}

.text3 {
  color: var(--g)
}
<div class="text1">Hello</div>
<div class="text2">Bye</div>
<div class="text3">World</div>
<button onclick="func()">Change</button>
Rana
  • 2,500
  • 2
  • 7
  • 28
  • Better (IMO) to use an array of objects, like `const props = [ { name: '--r', value: 'violet' }, { name: '--b', value: 'lightblue' }, { name: '--g', value: 'lightgreen' } ];` and `for (let prop of props) { root.style.setProperty(prop.name, prop.value); }`, but that's me :). – Heretic Monkey Oct 11 '21 at 21:03
  • Nice better than above one , Thanks for that . Will surely use that . @HereticMonkey Do you know any way to get all the variables without entering one by one like in above answer – Rana Oct 11 '21 at 21:06
  • 1
    You linked to the questions I use. – Heretic Monkey Oct 11 '21 at 21:08
1

Try this example.

  1. Initialize two arrays, one is empty for the current colors form the css and second for a new colors.
  2. Need to define how many variables, then get them and put in an empty array.
  3. Create two functions with loops, in the first we assign the new colors for variables, in the second reset.
const root = document.body;
// Colors arrays
const cssVarColors = [];
const newColors = ['brown', 'lightblue', 'lightgreen'];

// Create an array of variable colors from css.
const cssRootArray = document.styleSheets[0].cssRules;
for (const i of cssRootArray) {
// Check, if :root in the css.
  if (i.selectorText.includes('root')) {
    const rootArrayLength = i.style.length;
    for (let j = 0; j < rootArrayLength; j++) {
     const key = i.style[j];
     // Create object { key/variable : value/color }
     const value = getComputedStyle(root).getPropertyValue(key);
     cssVarColors.push({[key]: value});
    }
  }
}

// We change colors, with variable keys and indexes.
function changeColor() {
  for (const i in cssVarColors) {
   const key = Object.keys(cssVarColors[i]);
    // Check, if newColor array don't have a color.
    if (!newColors[i]) {
      return;
    }
   root.style.setProperty(key, newColors[i]);
  }
  variables();
}

change.addEventListener('click', changeColor);

  const root = document.body;
  // Colors arrays
  const cssVarColors = [];
  const newColors = ['brown', 'lightblue', 'lightgreen'];

  // Create an array of colors from a css variables file.
  const cssRootArray = document.styleSheets[0].cssRules;
  for (const i of cssRootArray) {
    // Check, if :root in the css.
    if (i.selectorText.includes('root')) {
      const rootArrayLength = i.style.length;
      for (let j = 0; j < rootArrayLength; j++) {
        const key = i.style[j];
        // Create object { key/variable : value/color }
        const value = getComputedStyle(root).getPropertyValue(key);
        cssVarColors.push({
          [key]: value,
        });
      }
    }
  }

  // We change colors, with variable keys and indexes.
  function changeColor() {
    for (const i in cssVarColors) {
      const key = Object.keys(cssVarColors[i]);
      // Check, if newColor array don't have a color.
      if (!newColors[i]) {
        return;
      }
      root.style.setProperty(key, newColors[i]);
    }
    variables();
  }
  // Can't separate in loop by [key, value],
  // because key has a specific value.
  function resetColor() {
    for (const i in cssVarColors) {
      if (!newColors[i]) {
        return;
      }
      const key = Object.keys(cssVarColors[i]);
      const value = Object.values(cssVarColors[i]);
      root.style.setProperty(key, value);
    }
    variables();
  }
  // Change button
  change.addEventListener('click', changeColor);
  // Reset button
  reset.addEventListener('click', resetColor);

  // extra for view
  cssVarColors.forEach(clr => {
    const el = document.createElement('span');
    el.textContent = JSON.stringify(clr);
    document.getElementById('variables').appendChild(el);
  });
:root {
  --r: red;
  --b: blue;
  --g: green;
  --m: magenta;
  --black: black;
}

.text1 {
  color: var(--r);
}

.text2 {
  color: var(--b);
}

.text3 {
  color: var(--g);
}


/* Extra style */
body {
  display: grid;
  grid-template-columns: 50px 150px;
  grid-template-rows: auto;
}

section {
  grid-column: 1;
}

#variables {
  grid-column: 2;
  display: flex;
  flex-direction: column;
}

span:nth-of-type(1) {
  border: 5px solid var(--r);
}

span:nth-of-type(2) {
  border: 5px solid var(--b);
}

span:nth-of-type(3) {
  border: 5px solid var(--g);
}

span:nth-of-type(4) {
  border: 5px solid var(--m);
}

span:nth-of-type(5) {
  border: 5px solid var(--black);
}
<section>
  <div class="text1">Hello</div>
  <div class="text2">Bye</div>
  <div class="text3">World</div>
</section>
<div id="variables"></div>
<button id="change">Change</button>
<button id="reset">Reset</button>
Anton
  • 8,058
  • 1
  • 9
  • 27
  • Can you tell about `Object.keys` a little – Rana Nov 05 '21 at 07:49
  • With **Object.keys** you can create an array of the keys. In the example above, i created an array, where the `key` of the object is a css variable. But, when we use `setProperty()` method, we need put *key* and *value*, so **Object.keys** can help get the *key*. You can see examples [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys). – Anton Nov 05 '21 at 09:41
  • Thanks for time and effort(+1), I am really glad you answered – Rana Nov 10 '21 at 05:58
1
const root = document.documentElement;

function changeVariables(...values) {
  const variables = ["--r", "--g", "--b"];
  for (const variable of variables) {
    root.style.setProperty(variable, values[variable.indexof(variables)]);
  }
}

//Simply execute function like this
changeVariables("#000", "#808080", "#FF0000");
1
  1. setting new-colors you want to change
  2. backup original-colors

Try the example bellow

var root    = document.querySelector(':root');
var $divEle = $("div[class^='text']");  // get all element with class starting with 'text'

var arrCssVar     = ['--r', '--b','--g'];
var backupOrgColor= {}
var newColor     = {'--r':'brown', '--b':'lightblue', '--g':'lightgreen'}; // ordering color what you want to change

// backup original colors
$divEle.map(function(i, v) {
  if(i < arrCssVar.length){
    var compStyle = getComputedStyle(this);
    // setting key/value pair to Obj
    backupOrgColor[arrCssVar[i]] = compStyle.getPropertyValue(arrCssVar[i]);
  }
});

// change color
function setNewColors() {
  arrCssVar.map(function (key, value) {
    //console.log(key + ": key :change: value : "+ newColor[key]);
    root.style.setProperty(key, newColor[key]);
  });
}

// reset original color
function resetColors() {
  arrCssVar.map(function (key, value) {
    //console.log(key + ": key :: value : "+ backupOrgColor[key]);
    root.style.setProperty(key, backupOrgColor[key]);
  });
}
:root {
  --r: red;
  --b: blue;
  --g: green;
}

.text1 {
  color: var(--r)
}

.text2 {
  color: var(--b)
}

.text3 {
  color: var(--g)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="text1">Hello</div>
<div class="text2">Bye</div>
<div class="text3">World</div>
<button onclick="setNewColors()">Change</button>
<button onclick="resetColors()">Orignal</button>
Dharman
  • 30,962
  • 25
  • 85
  • 135
CR_HONG
  • 13
  • 7
  • Thanks for time and effort(+1), I am really glad you answered. But I have to still enter all variables manually – Rana Nov 10 '21 at 09:19