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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.