Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert CSS-Object to Style Tag

Sometimes I'm forced to add CSS styles to the DOM programmatically (if you need a reason: imagine writing a small ui widget that comes with its own styling but is supposed to only consist of one single *.js file for easier handling). In this case I prefer defining the style in my script code with object notation instead of one large string that mixes rules, properties, and markup.

var thumbHoverStyle = {
    "background-color": "rgba(211, 211, 211, 0.5)",
    "cursor": "pointer"
};

versus

var thumbHoverStyle = "<style> .thumb:hover { background-color: rgba(211, 211, 211, 0.5); cursor: pointer; } </style>";

Such a css-object can easily be used with JQuery's .css() function, but the trouble starts as soon as I want to style a css pseudo-class (in my example :hover). In this case I can't use the JQuery .css() function and I fall back to inserting a corresponding style tag into my DOM.

var thumbHoverStyleTag = toStyleTag( { ".thumb:hover": thumbHoverStyle } );
_root.append(thumbHoverStyleTag);

I googled and stackoverflowed but could not find a utility function that converts my css-object into a style tag. In the end I wrote my own function (and I will probably provide it as an answer to this question), but I still want to know if there is a library function for this. What is the most elegant method to accomplish this?

Edit

My implementation in TypeScript:

function appendPxIfNumber(value: any): string
{
    return (typeof value === "number") ? "" + value + "px" : value;
}

function toStyleTag(rulesObj: any)
{
    var combinedRules: string = "";
    for (var selector in rulesObj)
    {
        var cssObject = rulesObj[selector];
        var combinedProperties = "";
        for (var prop in cssObject) {
            var value = cssObject[prop];
            combinedProperties += `${prop}: ${appendPxIfNumber(value)};` + "\n";
        }
        combinedRules += ` ${selector} {${combinedProperties}}` + "\n";
    }
    return $(`<style>${combinedRules}</style>`);
}

Usage example:

var styleTag = toStyleTag( { ".thumb": thumbStyle, ".thumb:hover": thumbHoverStyle } );
like image 467
Martin Avatar asked Dec 23 '16 14:12

Martin


2 Answers

Here's a working example which works with the origin style object: I'll convert the JSONinto CSS. and define a target which should be styled Keep in mind there is no selector which should be styled... So I added a targetSelector.

var targetSelector='.thumb:hover',
    styleObj = {
      "background-color": "rgba(211, 211, 211, 0.5)",
      "cursor": "pointer"
    },

    // Convert the JSON into CSS
    styleTagContent = JSON.stringify(styleObj,null,'\t')
                          .replace(/"/g,'')
                          .replace(/,\n/g,';')
                          .replace(/\}/g, ';}')  



  $('<style>'+targetSelector+styleTagContent+'</style>').appendTo('head');

Here's a working Plunk to see how it works.

like image 140
Bernhard Avatar answered Sep 20 '22 20:09

Bernhard


The methodology behind my function toStyleTag() is that it first iterates over styleTag using a for...in statement and checking if styleTag hasOwnProperty() of the selector. I then reduce() the array returned by executing Object.keys() on the object, styleTag[selector], which I concat() the accumulator by joining the currentValue (the property) and the styleTag[selector][currentValue]] (the rule) to be : separated. Doing so lets styles become an array of strings that are in the form of property: rule. This is now the format we need to use insertRule(), which we use this to insert the new CSS rule into the current style sheet. Notice that I am grabbing length of the cssRules to find the index + 1, so that we can insert this new rule at the very end of our style sheet to ensure it is not overwritten.

var styleSheet = document.styleSheets[0];

function toStyleTag(styleTag) {
  for(var selector in styleTag) {
    if(styleTag.hasOwnProperty(selector)) {
      let styles = Object.keys(styleTag[selector]).reduce(function(accumulator, currentValue) {
        return accumulator.concat([currentValue, styleTag[selector][currentValue]].join(':'));
      }, []);
      styleSheet.insertRule(selector + '{' + styles.join(';') + '}', styleSheet.cssRules.length);
    }
  }
}

var thumbHoverStyle = {
  'background-color': 'rgba(211, 211, 211, 0.5)',
  'cursor': 'pointer'
};

toStyleTag({ '.thumb:hover': thumbHoverStyle });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="thumb">
  <img src="http://placehold.it/350x350">
</div>
like image 26
Daerik Avatar answered Sep 17 '22 20:09

Daerik