Friday, June 30, 2006

Create CSS Class (JavaScript)

This function (createCSSClass) creates a new class with the given style. However, it does not work in version of Opera prior to 9, due to lack of support for document.styleSheets. For example, if you want all text in a pre element bold, you would do createCSSClass("pre", "font-weight: bold"). Note, that if you already have the rule defined, it will replace it, unless it is not a stylesheet with media type 'screen' (or media not set) or it is not the first linked/embedded screen stylesheet.

So for example, if you had the following:

<link rel="stylesheet" href="print.css" media="print" />
<link rel="stylesheet" href="screen.css" />
<style type="text/css">pre { border: 1px solid #000}</style>

When you do createCSSClass("pre", "font-weight: bold"), it will replace the rule in screen.css (or add it if it does not exist). Any styles defined in print.css and the following <style> will not be replaced.

The function itself:

function createCSSClass(selector, style)
{
 // using information found at: http://www.quirksmode.org/dom/w3c_css.html
 // doesn't work in older versions of Opera (< 9) due to lack of styleSheets support
 if(!document.styleSheets) return;
 if(document.getElementsByTagName("head").length == 0) return;
 var stylesheet;
 var mediaType;
 if(document.styleSheets.length > 0)
 {
  for(i = 0; i<document.styleSheets.length; i++)
  {
   if(document.styleSheets[i].disabled) continue;
   var media = document.styleSheets[i].media;
   mediaType = typeof media;
   // IE
   if(mediaType == "string")
   {
    if(media == "" || media.indexOf("screen") != -1)
    {
     styleSheet = document.styleSheets[i];
    }
   }
   else if(mediaType == "object")
   {
    if(media.mediaText == "" || media.mediaText.indexOf("screen") != -1)
    {
     styleSheet = document.styleSheets[i];
    }
   }
   // stylesheet found, so break out of loop
   if(typeof styleSheet != "undefined") break;
  }
 }
 // if no style sheet is found
 if(typeof styleSheet == "undefined")
 {
  // create a new style sheet
  var styleSheetElement = document.createElement("style");
  styleSheetElement.type = "text/css";
  // add to <head>
  document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
  // select it
  for(i = 0; i<document.styleSheets.length; i++)
  {
   if(document.styleSheets[i].disabled) continue;
   styleSheet = document.styleSheets[i];
  }
  // get media type
  var media = styleSheet.media;
  mediaType = typeof media;
 }
 // IE
 if(mediaType == "string")
 {
  for(i = 0;i<styleSheet.rules.length;i++)
  {
   // if there is an existing rule set up, replace it
   if(styleSheet.rules[i].selectorText.toLowerCase() == selector.toLowerCase())
   {
    styleSheet.rules[i].style.cssText = style;
    return;
   }
  }
  // or add a new rule
  styleSheet.addRule(selector,style);
 }
 else if(mediaType == "object")
 {
  for(i = 0;i<styleSheet.cssRules.length;i++)
  {
   // if there is an existing rule set up, replace it
   if(styleSheet.cssRules[i].selectorText.toLowerCase() == selector.toLowerCase())
   {
    styleSheet.cssRules[i].style.cssText = style;
    return;
   }
  }
  // or insert new rule
  styleSheet.insertRule(selector + "{" + style + "}", styleSheet.cssRules.length);
 }
}

2 comments:

Stephen Clay said...

tested in Opera 9? It supports document.styleSheets.

Anonymous said...

This works quite well. I did discover that IE does not like multiple definitions defined using the same string. So for ("div.class1, div.class2", css) I needed two identical entries. One for ("div.class1", css) and one for ("div.class2", css). Not a big deal, and something the method could probably handle, but I haven't devoted the time to writing a fix since the work around is easy enough. Just thought I would let folks know.