views:

3904

answers:

8

I'm looking for a way to insert a <style> tag into an HTML page with javascript.

The best way I found so far:

var divNode = document.createElement("div");
divNode.innerHTML = "<br><style>h1 { background: red; }</style>";
document.body.appendChild(divNode);

This works in Firefox, Opera and Internet Explorer but not in Google Chrome. Also it's a bit ugly with the <br> in front for IE.

Does anyone know of a way to create a <style> tag that

  1. is nicer
  2. works with Chrome?

Or maybe

  1. this is a non-standard thing I should avoid
  2. three working browsers are great and who uses Chrome anyway?

I appreciate any advice on this.

+11  A: 

I'm assuming that you're wanting to insert a style tag versus a link tag (referencing an external CSS), so that's what the following example does:

<html>
 <head>
  <title>Example Page</title>
 </head>
 <body>
  <span>
   This is styled dynamically via JavaScript.
  </span>
 </body>
 <script type="text/javascript">
   var styleNode = document.createElement('style');
   styleNode.type = "text/css";
   // browser detection (based on prototype.js)
   if(!!(window.attachEvent && !window.opera)) {
     styleNode.styleSheet.cssText = 'span { color: rgb(255, 0, 0); }';
   } else {
     var styleText = document.createTextNode('span { color: rgb(255, 0, 0); } ');
     styleNode.appendChild(styleText);
   }
   document.getElementsByTagName('head')[0].appendChild(styleNode);
 </script>
</html>

Also, I noticed in your question that you are using innerHTML. This is actually a non-standard way of inserting data into a page. The best practice is to create a text node and append it to another element node.

With respect to your final question, you're going to hear some people say that your work should work across all of the browsers. It all depends on your audience. If no one in your audience is using Chrome, then don't sweat it; however, if you're looking to reach the biggest audience possible, then it's best to support all major A-grade browsers

Tom
This is basically the same as my solution; neither of them works in IE!
Christoph
Appending a text node doesn't work in IE. But putting the style element in the head makes my solution work in Chrome! :-)
for IE, use `styleNode.styleSheet.cssText = ...`
Christoph
Alright, cool. I updated the above code to include browser detection so that it will apply the dynamic style addition properly.
Tom
Why are you manipulation the DOM before checking if it has been loaded?
Luca Matteis
@sktrdie: afaik `head` is guaranteed to exist when a `script` is executed - as long as the HTML is well-formed, the `head` element will always precede the `script` element
Christoph
(Very late comment) "It all depends on your audience. If no one in your audience is using Chrome, then don't sweat it"This attitude is what leads to people being locked into IE6 for *years* after IE7 and even IE8 are available. "But the web application I have to use **only** works in IE6"
Stephen P
Yep, you're right and I'm not necessarily advocating doing that either (hence the whole: "if you're looking to reach the biggest audience possible, then it's best to support all major A-grade browsers") but knowing the audience for your platform is important.
Tom
A: 

An example that works and are compliant with all browsers :

var ss = document.createElement("link");
ss.type = "text/css";
ss.rel = "stylesheet";
ss.href = "style.css";
document.getElementsByTagName("head")[0].appendChild(ss);
belaz
wrong element: `style` has no `rel` or `href` attribute - did you mean `link`?
Christoph
Right, corrected ;D
belaz
+9  A: 

Why do you add the element to the body and not the head?

This was tested in IE, FF and Opera:

var head = document.getElementsByTagName('head')[0],
    style = document.createElement('style'),
    rules = document.createTextNode('h1 { background: red; }');

style.type = 'text/css';
if(style.styleSheet)
    style.styleSheet.cssText = rules.nodeValue;
else style.appendChild(rules);
head.appendChild(style);
Christoph
document.body is much nicer than document.getElementsByTagName('head')[0], unfortunately it doesn't work in Chrome
@Arend: HTML doesn't allow `style` elements in the body, so browsers are free to ignore them...
Christoph
+1  A: 

Here's a script which adds IE-style createStyleSheet() and addRule() methods to browsers which don't have them:

if(typeof document.createStyleSheet === 'undefined') {
    document.createStyleSheet = (function() {
        function createStyleSheet(href) {
            if(typeof href !== 'undefined') {
                var element = document.createElement('link');
                element.type = 'text/css';
                element.rel = 'stylesheet';
                element.href = href;
            }
            else {
                var element = document.createElement('style');
                element.type = 'text/css';
            }

            document.getElementsByTagName('head')[0].appendChild(element);
            var sheet = document.styleSheets[document.styleSheets.length - 1];

            if(typeof sheet.addRule === 'undefined')
                sheet.addRule = addRule;

            if(typeof sheet.removeRule === 'undefined')
                sheet.removeRule = sheet.deleteRule;

            return sheet;
        }

        function addRule(selectorText, cssText, index) {
            if(typeof index === 'undefined')
                index = this.cssRules.length;

            this.insertRule(selectorText + ' {' + cssText + '}', index);
        }

        return createStyleSheet;
    })();
}

You can add external files via

document.createStyleSheet('foo.css');

and dynamically create rules via

var sheet = document.createStyleSheet();
sheet.addRule('h1', 'background: red;');
Christoph
+1  A: 

Here is a variant for dynamically adding a class

function setClassStyle(class_name, css) {
  var style_sheet = document.createElement('style');
  if (style_sheet) {
    style_sheet.setAttribute('type', 'text/css');
    var cstr = '.' + class_name + ' {' + css + '}';
    var rules = document.createTextNode(cstr);
    if(style_sheet.styleSheet){// IE
      style_sheet.styleSheet.cssText = rules.nodeValue;
    } else {
      style_sheet.appendChild(rules);
    }
    var head = document.getElementsByTagName('head')[0];
    if (head) {
      head.appendChild(style_sheet);
    }
  }

}

Michael
A: 

Hello Michael. I've used your function and it works great! Thanks alot!!!

Now I have another problem. It can happen that the same class will be added more then once, though it is not needed...do you have any idea how to check if a style is already added ? thanks again!

countdown
A: 

All good, but for styleNode.cssText to work in IE6 with node created by javascipt, you need to append the node to the document before you set the cssText;

further info @ http://msdn.microsoft.com/en-us/library/ms533698%28VS.85%29.aspx

Tony
A: 

@Christoph - Thank you for your solution. You sir, are a god. Was pulling my hair on this issue trying to set the HTML of the style element directly - bombing in IE but working in all other browsers.

Thanks!