16

We'd like to convert a CSS style entered as string into a JS object.

E.g.,

 var input = " border:solid 1px; color:red ";

expected JS object :

 {
    border:"solid 1px",
    color:"red"
 }

Of course the number of style entries is unlimited as well as the names of style (border, color, font, z-index, etc...). Thx.

2
  • Testing shows, that only jAndy's and mjac's answers are working properly. The others forgot about whitespaces. Commented Mar 1, 2012 at 20:24
  • jsperf.com/style-tag-to-object comparison of some of the answers posted Commented Feb 19, 2017 at 13:00

11 Answers 11

10

A very simple one:

var regex = /([\w-]*)\s*:\s*([^;]*)/g;
var match, properties={};
while(match=regex.exec(cssText)) properties[match[1]] = match[2].trim();

https://regex101.com/r/nZ4eX5/1

Sign up to request clarification or add additional context in comments.

1 Comment

Just a note: it shouldn't matter much, but be aware that match[2] won't be trimmed on the right side. :-)
8

Stylesheet string to element style using JavaScript

Use just the string, CSSStyleDeclaration.cssText:

const styles = "color: #222; background: orange; font-size: 2em;";
document.querySelector("#test").style.cssText = styles;
<div id="test">Lorem Ipsum</div>

JavaScript Implementation

If you need to convert a CSS style string to Object:

const css2obj = css => {
  const r = /(?<=^|;)\s*([^:]+)\s*:\s*([^;]+)\s*/g, o = {};
  css.replace(r, (m,p,v) => o[p] = v);
  return o;
};

const cssObj = css2obj("color: #222; background: orange; font-size: 2em;");
console.log(cssObj);

// To apply use:
const elTest = document.querySelector("#test");
Object.assign(elTest.style, cssObj);
<div id="test">Lorem Ipsum</div>

In case you want to convert dash-case CSS properties to JS representations in camelCase, instead of p use p.replace(/-(.)/g, (m,p) => p.toUpperCase())


Oldschool JS:

function cssToObj(css) {
  var obj = {}, s = css.toLowerCase().replace(/-(.)/g, function (m, g) {
    return g.toUpperCase();
  }).replace(/;\s?$/g,"").split(/:|;/g);
  for (var i = 0; i < s.length; i += 2) {
    obj[s[i].replace(/\s/g,"")] = s[i+1].replace(/^\s+|\s+$/g,"");
  }
  return obj;
}

console.log( cssToObj("color: #222; background: orange; font-size: 2em;") );

5 Comments

The first solution (JavaScript Implementation), unfortunately, does not yet work in Firefox
The javascript implementation doesn't work on safari because of lacking support to the lookbehind operator (?<=
@MazarD Some newest Safari version?
@RokoC.Buljan Yes, it's not supported by any safari version: caniuse.com/js-regexp-lookbehind Safari is the new IE :(
@MazarD yeah, looks like. In that case the "Old-school JS" solution would work I think.
7

You could use the Javascript split function: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split

First split the string with ; as the separator, and then for each result split with :, placing the items in an object as you go.

e.g.

var result = {},
    attributes = input.split(';');

for (var i = 0; i < attributes.length; i++) {
    var entry = attributes[i].split(':');
    result[entry.splice(0,1)[0]] = entry.join(':');
}

3 Comments

Won't work if there's a ':' in the CSS value. E.g. background-image: url('http://example.com/image.png'). Fixed by splitting only on the first ':', e.g. entry = attributes[i].split(/:(.+)/).
Short and simple but it doesn't handle spaces that well. Common css usage has a space after the colon, e.g 'background: red' ... but this produces { background: ' red' } (notice the unnecessary space before red)
This fails if ; exists in a content: "..." property
5

In a functional form:

var styleInput = " border:solid 1px; color:red ";

var result = styleInput.split(';').reduce(function (ruleMap, ruleString) {
    var rulePair = ruleString.split(':');
    ruleMap[rulePair[0].trim()] = rulePair[1].trim();

    return ruleMap;
}, {});

Trim the strings before using them as object keys.

3 Comments

Trim isn't available in all browsers
@jondavidjohn Not all browsers are created equal. ({" trimmed":1}).trimmed -> undefined
Trim requires JS 1.8.1 but that's outside the scope of the question.
3

All the answers seem to need a lot of splitting- why not do a match and get all the pairs in one go?

function cssSplit(str){
    var O= {},
    S= str.match(/([^ :;]+)/g) || [];
    while(S.length){
        O[S.shift()]= S.shift() || '';
    }
    return O;
}

2 Comments

What's wrong with splitting? Yes, you could argue it takes (a bit) longer, but I doubt that we are talking about thousands of css properties here. Even then, I bet that your match is similar computational intensity to S=str.split(/[ :;]+/);.
This solution will take longer, cause shift is slow. Code has less size, though.
2

Just for fun and for completeness…

I haven't checked cross-browser compatibility (only tried in Chrome), and it has some quirks:

var input = "font-weight:bold; color: blue; margin: 0 15px";

var e = document.createElement("div");
e.setAttribute("style", input);

var output = {};

for (var i = 0; i < e.style.length; i++) {
  var name = e.style[i];
  var value = e.style.getPropertyValue(name);
  output[name] = value;
}

The quirk is that even though we passed in a single margin declaration, we get an object like

{
  color: "blue",
  font-weight: "bold",
  margin-bottom: "0px",
  margin-left: "15px",
  margin-right: "15px",
  margin-top: "0px",
}

This might be a good or a bad thing depending on what you're after.

Comments

1

If you want a tagged template literal syntax that works easily with React, you could do

const camelCase = str => str.replace(/-(.)/g, (_,p) => p.toUpperCase())

const css2obj = (strings, ...vals) => {
  const css = strings.reduce((acc, str, i) => acc + str + (vals[i] || ''), '')
  const r = /(?<=^|;)\s*([^:]+)\s*:\s*([^;]+)\s*/g, o = {}
  css.replace(r, (m,p,v) => o[camelCase(p)] = v)
  return o
}

const center = 'center'

const reactInlineCSS = css2obj`
  align-items: ${center};
  justify-content: ${center};
`

console.log(reactInlineCSS)

Comments

0

something like this should get you pretty close:

var input = " border:solid 1px; color:red ";
var output = '{' + input.replace(/([\w-.]+)\s*:([^;]+);?/g, '\n    $1:"$2",') + '\n}';

...turns

" border:solid 1px; color:red "

into

{ 
    border:"solid 1px", 
    color:"red ",
}

Comments

0

This is my function to convert CSS string to object:

function cssConverter(style) {
    var result = {},
        attributes = style.split(';'),
        firstIndexOfColon,
        i,
        key,
        value;

    for(i=0; i<attributes.length; i++) {
        firstIndexOfColon = attributes[i].indexOf(":");
        key = attributes[i].substring(0, firstIndexOfColon);
        value = attributes[i].substring(firstIndexOfColon + 1);

        key = key.replace(/ /g, "");
        if(key.length < 1){
            continue;
        }

        if(value[0] === " "){
            value = value.substring(1);
        }

        if(value[value.length - 1] === " "){
            value = value.substring(0, value.length - 1);
        }

        result[key] = value;
    }

    return result;
};

Comments

0

Now you can use a third-party library such as style-to-object. For example:

import parse from 'style-to-object';

const input = " border:solid 1px; color:red ";
parse(input); // => {border: "solid 1px", color: "red"}

Comments

0

The following function works even when there are ; inside single or double quotes. Ex: content: "data;".

function inlineStylesToObject(styles: string) : Record<string, string> {

  const regex = /([\w-]+)\s*:\s*((?:(?:"[^"]+")|(?:'[^']+')|[^;])*);?/g;

  const obj : Record<string, string> = {};

  let match;
  while (match = regex.exec(styles)) {
     obj[match[1]] = match[2].trim();
  }

  return obj;

}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.