5

Is it at all possible to dynamically style elements using CSS properties that are stored within a JS object?

For example, changing both the width and background of a simple <div> element:

<div id="box"></div>
<button id="btn">click me</button>

The box is initally styled with:

div {
  background: grey;
  width: 100px;
  height: 100px;
}

The box will be restyled when clicking the button element as so:

btn.addEventListener('click', () => {
  // Code to change box style here...
}

I have seen the use of setAttribute('style', 'some style stuff here');, however I have come to understand that this will simply replace ALL of the style attribute associated with the element, rather than appending/changing the properties defined within :-(

My goal here is to do hold CSS properties in a JS object such as:

const myStyle = {
  'background': 'green',
  'width': '20px'
}

and apply this to the element.

I know this can be done by holding the properties in another CSS class named something like '.box-transform' and then adding that to the classList of the element... But I am wanting to do this through JS.

My initial attempt was something along the lines of:

btn.addEventListener('click', () => {
  for (let [key, val] of Object.entries(myStyle)) {
    console.log(`${key}: ${val}`)
  box.setAttribute('style', `${key}: ${val}`)
}
});

However I was running into the issue of the overriding nature of setAttribute...

const btn = document.getElementById('btn');
const box = document.getElementById('b');

const myobj = {
  'width': '20px',
  'background': 'yellow'
};

btn.addEventListener('click', () => {
  for (let [key, val] of Object.entries(myobj)) {
    console.log(`${key}: ${val}`)
  box.setAttribute('style', `${key}: ${val}`)
}
});
.box {
  width: 300px;
  height: 300px;
  background: grey;
}
<div class="box" id="b"></div>
<button id="btn">click</button>
physicsboy
  • 5,656
  • 17
  • 70
  • 119
  • 2
    Just use `box.style[key] = val;` instead. (without that, the solution would be to combine all properties into a single CSS string, then set that as `style`) –  Dec 05 '19 at 10:47
  • Ah amazing! I knew it would be something like this. I had tried `.style.${key}` before like a dunce... If you post as an answer, I will give you some karma ;-) – physicsboy Dec 05 '19 at 10:55
  • FYI IE won't recognize these `() => {}` ````${key}```` – Abhishek Pandey Dec 05 '19 at 11:04
  • @AbhishekPandey that's what Babel is for ;-) – physicsboy Dec 05 '19 at 11:09
  • 1
    Does this answer your question? [How can I set multiple CSS styles in JavaScript?](https://stackoverflow.com/questions/3968593/how-can-i-set-multiple-css-styles-in-javascript) –  Dec 05 '19 at 11:14
  • @ChrisG Now I understand the `cssText` property, yes it does. I saw that answer earlier and was confused as the CSS properties specified were all written inline and looked horrendous... A colleague pointed out their own usage using backticks to place each property on a separate line. Looks much cleaner! – physicsboy Dec 05 '19 at 11:19
  • You have to scroll down a bit to [this answer](https://stackoverflow.com/a/3968677/5734311) which should be the accepted one. –  Dec 05 '19 at 11:20

3 Answers3

4

const btn = document.getElementById('btn');
const box = document.getElementById('b');

const myobj = {
  'width': '200px',
  'background': 'yellow'
};

btn.addEventListener('click', () => {
  for (let [key, val] of Object.entries(myobj)) {
    box.style[key] = val;
  }
});
.box {
  width: 300px;
  height: 300px;
  background: grey;
}
<div class="box" id="b"></div>
<button id="btn">click</button>
abdelhedi hlel
  • 2,903
  • 1
  • 16
  • 20
  • Good answer obviously, but not upvoting this since question is a dupe. –  Dec 05 '19 at 11:23
1

You can first generate the CSS as string then use cssText property:

const btn = document.getElementById('btn');
const box = document.getElementById('b');

const myobj = {
  'width': '20px',
  'background': 'yellow'
};

btn.addEventListener('click', () => {
  var cssText ='';
  for (let [key, val] of Object.entries(myobj)) {
    cssText += `${key}: ${val};`  
  }
  box.style.cssText =  cssText;
});
.box {
  width: 300px;
  height: 300px;
  background: grey;
}
<div class="box" id="b"></div>
<button id="btn">click</button>

You can also update the style with the object key and value inside the loop:

const btn = document.getElementById('btn');
const box = document.getElementById('b');

const myobj = {
  'width': '20px',
  'background': 'yellow'
};

btn.addEventListener('click', () => {
  var cssText ='';
  for (let [key, val] of Object.entries(myobj)) {
    box.style[key] = val;  
  }
});
.box {
  width: 300px;
  height: 300px;
  background: grey;
}
<div class="box" id="b"></div>
<button id="btn">click</button>
Mamun
  • 66,969
  • 9
  • 47
  • 59
  • 2
    This works but setting `box.style[key]` directly is much more readable (and better practice). –  Dec 05 '19 at 11:03
  • 1
    @ChrisG, included that as part pf the answer, thanks:) – Mamun Dec 05 '19 at 11:10
  • @ChrisG applying a style in each iteration is segnificantly more expensive than concatenating in each iteration and applying only at the end. This may not matter in many cases, but should be noted for cases when efficiency matters. – H K Jul 08 '22 at 16:00
1

Here are a number of ways of we can apply styles from a JavaScript object to an element's style attribute.

For the most concise syntax where ES6 is acceptable we can use Object.assign:

Object.assign(elem.style, stylesObject);

We can also convert the styles object to an array with Object.entries then use map to convert each key-value to an appropriate string and finally, join the strings together using join:

elem.style = Object.entries(stylesObject).map(x => `${x[0]}:${x[1]}`).join(';');

We can also concatenate a string using various for loops.

We can use for in:

let str = '';
for (let style in stylesObject) {
    str += `${style}:${stylesObject[style]};`;
}
elem.style = str;

or we can use forEach:

let str = '';
Object.entries(stylesObject).forEach(style => {
    str += `${style[0]}:${style[1]};`;
})
elem.style = str;

or we can use for of:

let str = '';
for (let style of Object.entries(stylesObject)) {
    str += `${style[0]}:${style[1]};`;
}
elem.style = str;

and finally, we can also use a regular good ol' for loop:

const stylesArray = Object.entries(stylesObject);
let str = '';
for (let x = 0; x < stylesArray.length; x++) {
    str += `${stylesArray[x][0]}:${stylesArray[x][1]};`;
}
elem.style = str;

You can check out this benchmark tests I put together to see how these options affect performance.

[For the curious, you can see version 2 of the benchmark tests, where I added more variations (most of the additions is just changing Object.entries to Object.keys).]

I left out the option of applying the style in each loop iteration instead of concatenating to a string, because, as you can see in the benchmarks, that is significantly more expensive.

H K
  • 1,062
  • 8
  • 10