Monday, September 14, 2009

Modifying CSS in Javascript

Modify CSS class/selector value of property/key by using Javascript without changing for each element style.

In some cases, we need to change a CSS property for a CSS class to make all elements uses the class will take into effect for the property changed. If you are able to author and modify the CSS and the web page. You can use two or more classes that refer to the same styling but little different change to its property. So whenever you want to change the styling, just swap class name for any element you want.

Let say you have a table with all rows have class named .RowStyle. So the class may look like this:

  color: #000000;
  text-align: center;

Now you just want to change only a color property for all rows. So you need to define new class that have nearly same properties like this:

  color: #FF0000;
  text-align: center;

Then you need to find all rows and replace all class RowStyle with RowStyleRed because the reason you don't need to change back to the old class and still assume both class refer to the same styling but just changing a little bit.

In this case, it is good if there is an simple way to change a property is specific class so we don't need to find back all element that use the class. That the thing I want to discover.

My Situation:

In my situation, I have no power to change it. For example, like this blog, I want to change the height of Reaction features (I rename it as Feedback as shown bellow). It was defined in the ".reactions-iframe". The CSS classes is auto-generated just like the HTML and JavaScript.

This is the original properties of the class:
  background:transparent none repeat scroll 0 0;
  border:0 none;
I try to add the class again at the bottom of the HTML to overwrite the height like this:

However it is not overwrite the height only but the entire class. Thus, all other properties are removed. So I try to find the solution by modifying CSS class properties in JavaScript. Note that changing the value in CSS class property is not same like changing style for a DOM element which is just for that element only. Change in CSS class will take effect to all elements that using the class.

The Solution:

I found that there is no easy way to do. I don't want to use jQuery in my case since I just want to solve this CSS problem only. This post, Altering CSS Class Attributes with JavaScript give me an idea on how
possible and cross-browser compatible to modify CSS property by using JavaScript. No simple command like changing style of an element like document.getElementById('mydiv').style.height = '5.5em'. Everything is under document.styleSheets and there is a different version depend on browser. document.styleSheets[i].cssRules is used by FireFox while document.styleSheets[i].rules is used by IE where i is the style sheet document index number need to enumerate.

To make it easier we can specify document.styleSheets[i][cssRules] where cssRules is a string variable that can be "rules" or "cssRules" depend on browser. It will return an array of selector or class defined in the style sheet document. So document.styleSheets[i][cssRules][i] will return an object for single selector or class. Not so fast, a lot things to know. document.styleSheets[i][cssRules][i].selectorText is the selector or class name and document.styleSheets[i][cssRules][i].style['height'] will give you access to the height property just for example.

Here is the function:
function SetCSS(theClass, property, value) {
            var cssRules;
            var added = false;

            // Find the compatibility only one time.
            if (document.styleSheets.length > 0) {
                if (document.styleSheets[0]['rules']) {
                    cssRules = 'rules'; // IE compatible
                } else if (document.styleSheets[0]['cssRules']) {
                    cssRules = 'cssRules'; // FireFox compatible
                } else return; // unknown compatibility
            } else return; // no style sheet found

            for (var i = 0; i < document.styleSheets.length; i++) {
                for (var j = 0; j < document.styleSheets[i][cssRules].length; j++) {
                    if (document.styleSheets[i][cssRules][j].selectorText == theClass) {
                        if (document.styleSheets[i][cssRules][j].style[property]) {
                            document.styleSheets[i][cssRules][j].style[property] = value;
                            added = true;

                if (added) {
                    break; // break the outer for loop
                else {
                    if (document.styleSheets[i].insertRule) {
                        document.styleSheets[i].insertRule(theClass + ' { ' + property + ': ' + value + '; }', document.styleSheets[i][cssRules].length);
                    } else if (document.styleSheets[i].addRule) {
                        document.styleSheets[i].addRule(theClass, property + ': ' + value + ';');
This function need an enhancement since setting CSS each time will enumerate in the style sheet documents. Using like a hash table for lookup will make the enumeration faster since class name or selector is unique for a page and we can map the name to the style sheet document index. I use this function successfully in my local. However, using this function in other than localhost like in the blogger will throw an error Security error code 1000 NS_ERROR_DOM_SECURITY_ERR in FireFox. IE7 has no problem. I still inspecting why this happened. FireFox dont allow this way. I fustraited, the hole day I wasting for this endless solution. Quickly I change to the other idea.

The Real Solution:

We can't modify the defined class instead of we need to change for each object. We can reduce performance by modifying the class since it will update for all object that use the class but the allowable ways is just change the style the objects. Since I lost my day now I need the fast solution by just change it for each object.

Here is the code to change height of 'iframe' that use class 'reactions-iframe':
function SetReactionsHeight(Height) {
  var iframes = document.getElementsByTagName('iframe');
  for (var i in iframes) {
    if (iframes[i].className == 'reactions-iframe') {
      iframes[i].style.height = Height;
I dont like this solution since it will enumerate all element (if we dont know the tag name) and will cause performance hit. To enumerate for each element available in the html, use document.getElementsByTagName('*') like in this post  document.getAllElements() - or equivalent.
However it is an easy solution for simple page. If you plan using JQuery $('.reactions-iframe').css('height','5.5em') is the answer as stated How can I change the css class rules using jQuery. It just use 45 letters in single line of code to do the same like the real solution.

No comments:

Post a Comment