neaumusic
neaumusic

Reputation: 10474

How to convert a JSON style object to a CSS string?

I wanted to set my element's style as such:

this.refs.element.style = {
    ...this.props.style,
    background: 'blue',
};

But apparently you can't use an object to set the ref's style. I have to use a CSS style string with ; separating the prop:values

I'm aware that most people would set style in the render function, but for performance reasons, I can't repeatedly re-render.

Upvotes: 17

Views: 28224

Answers (6)

Felippe Regazio
Felippe Regazio

Reputation: 389

TL;DR: The problem is that you are overwriting the entire "style" property of the element and losing its prototype and methods. You must add your style object without change the entire property. If you want to apply an object-like style to a DOM element, just do:

Object.assign(this.refs.element.style, {
    background: 'blue',
    color: 'white',
    /** style properties:values goes here */
});

Explanation: The property "style" is an instance of the "CSSStyleDeclaration" interface. If you overwrite the interface it wont be a "CSSStyleDeclaration" anymore. It works when you set a string as value because javascript will pass the string directly to the element, without process anything.

CSSStyleDeclaration Reference Doc: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration

If you want to do a test, go to your navigator > inspector > console and paste the code below:

const p1 = document.createElement('p');
p1.style = { color: 'blue' };

const p2 = document.createElement('p');
Object.assign(p2.style, { color: 'blue' });

console.log(p1);
console.log(p2);

The output will be:

<p style=""></p>
<p style="color: blue;"></p>

Upvotes: 0

Tofandel
Tofandel

Reputation: 3585

Adding to the great answer of @Artem Bochkarev

I'm adding a snippet to do the opposite conversion as well (string to object) which may come in handy to anyone stumbling here

const style = {
  width: '1px',
  height: '1px',
  backgroundColor: 'red',
  transform: 'rotateZ(45deg)',
};
const styleToString = (style) => {
  return Object.keys(style).reduce((acc, key) => (
    acc + key.split(/(?=[A-Z])/).join('-').toLowerCase() + ':' + style[key] + ';'
  ), '');
};
const stringToStyle = (style) => {
  const styles = {};
  style.split(';').forEach((s) => {
    const parts = s.split(':', 2);
    if (parts.length > 1) {
      styles[parts[0].trim().replace(/-([a-z])/ig, (_, l) => l.toUpperCase())] = parts[1].trim();
    }
  });
  return styles;
};

console.log(styleToString(style));
// output - "width:1px;height:1px;background-color:red;transform:rotateZ(45deg);"

console.log(stringToStyle(styleToString(style)));

Upvotes: 1

neaumusic
neaumusic

Reputation: 10474

A performant answer is to map and join the Object.entries with semicolons:

const style = {
  ...this.props.style,
  background: 'blue',
};

const styleString = (
  Object.entries(style).map(([k, v]) => `${k}:${v}`).join(';')
);

It unwraps background:'blue', to background:blue; which works well for CSS


To replace any capital letter with dash lowercase letter

k = k.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);

Upvotes: 44

Chetan Jain
Chetan Jain

Reputation: 241

Use https://www.npmjs.com/package/json-to-css. Note it will not add a semicolon to the last property to fix it you can beautify it with https://www.npmjs.com/package/cssbeautify Example


    const cssbeautify = require('cssbeautify')

    const Css = require('json-to-css')

    const json = {
        "h1": {
            "font-size": "18vw",
            "color": "#f00"
        },
        ".btn": {
            "font-size": "18vw",
            "color": "#f00"
        }
    }


    const r = Css.of(json)
    console.log(r)

    const beautified = cssbeautify(r, {
        autosemicolon: true
    })

    console.log(beautified)

Result


  console.log src/utils/playground/index.spec.ts:22 // json-to-css
    h1{font-size:18vw;color:#f00}
    .btn{font-size:18vw;color:#f00}

  console.log src/utils/playground/index.spec.ts:29 // cssbeautify
    h1 {
        font-size: 18vw;
        color: #f00;
    }
    
    .btn {
        font-size: 18vw;
        color: #f00;
    }
   

Upvotes: 4

Artem Bochkarev
Artem Bochkarev

Reputation: 1360

this solution works in IE and handles camelCase keys like backgroundColor

const style = {
    width: '1px',
    height: '1px',
    backgroundColor: 'red',
    transform: 'rotateZ(45deg)',
}
const styleToString = (style) => {
    return Object.keys(style).reduce((acc, key) => (
        acc + key.split(/(?=[A-Z])/).join('-').toLowerCase() + ':' + style[key] + ';'
    ), '');
};

console.log(styleToString(style));
// output - "width:1px;height:1px;background-color:red;transform:rotateZ(45deg);"

Upvotes: 12

Kent Wood
Kent Wood

Reputation: 1522

the css function in @material-ui/system can help you out check more info here

import React from 'react';
import styled, { ThemeProvider } from 'styled-components';
import NoSsr from '@material-ui/core/NoSsr';
import { createMuiTheme } from '@material-ui/core/styles';
import { compose, spacing, palette, css } from '@material-ui/system';

const Box = styled.div`
  ${css(
    compose(
      spacing,
      palette,
    ),
  )}
`;

const theme = createMuiTheme();

export default function CssProp() {
  return (
    <NoSsr>
      <ThemeProvider theme={theme}>
        <Box color="white" css={{ bgcolor: 'palevioletred', p: 1, textTransform: 'uppercase' }}>
          CssProp
        </Box>
      </ThemeProvider>
    </NoSsr>
  );
}

Upvotes: -1

Related Questions