6

related question with unsatisfactory answers: Changing CSS Values with Javascript

Here's what I want to do. I'd like to be able to set a page element's class to some value, and have the browser animate it, for instance a table td whose background will be set to rgb(255*(cos(time)*.5+.5),0,0).

I can easily do this for a single element by setting a timer. But I might want to have multiple elements have this property, and having to keep track of a timer for each one isn't a great idea.

So what I'd like to do is to be able to change a CSS value from javascript.

I am looking at the code here and it strikes me as being a bit too heavy-handed for me to use from a timer. From the looks of it, each call I make to a function like changecss has me appending an entire css rule to every single stylesheet. This is going to majorly pollute the stylesheets internally and will probably result in a runaway memory leak if I call it in a loop every 33ms.

So I'm looking for a solution that will let me zero in on the rgb color setting within the css entry corresponding to a particular class. And modify it. I don't actually need to retrieve the original setting in this case. Can this be done? I'd like to support IE9 if possible, though IE 6,7,8 already screws up my project so much that I've given up on them.

Community
  • 1
  • 1
Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • 1
    A year and a half later, and also probably back then, the better way to do this sort of thing is with CSS3 transitions. – Steven Lu Mar 21 '13 at 16:58

2 Answers2

10

That answer has the basics. As a general solution, you need to find the last occurence of the class in a style sheet (i.e. iterate over all the style sheets to find the last occurence of the class rule), then either add a new rule after that or modify the last rule.

If you know the class only exists once, you can just get the first occurence and modify that. Or if you know the class isn't in the style sheets at all, just add it anywhere (say to the last style sheet) and go from there.

You could easily animate it by storing a reference to the rule and change it over time with setTimeout.

For example, my library routine for changing a css rule is:

/* Replace the cssText for rule matching selectorText with value
** Changes all matching rules in all style sheets
*/
function modifyStyleRule(selectorText, value) {
  var sheets = document.styleSheets;
  var sheet, rules, rule;
  var i, j, k, l;

  for (i=0, iLen=sheets.length; i<iLen; i++) {
    sheet = sheets[i];

    // W3C model
    if (sheet.cssRules) {
      rules = sheet.cssRules;

      for (j=0, jLen=rules.length; j<jLen; j++) {
        rule = rules[j];

        if (rule.selectorText == selectorText) {
          removeRule(sheet, rule);
          addRule(sheet, selectorText, value);
        }
      }

    // IE model
    } else if (sheet.rules) {
      rules = sheet.rules;

      for (k=0, kLen=rules.length; k<kLen; k++) {
        rule = rules[k];

        // An alternative is to just modify rule.style.cssText,
        // but this way keeps it consistent with W3C model
        if (rule.selectorText == selectorText) {
          removeRule(sheet, rule);
          addRule(sheet, selectorText, value);

          // Alternative
          // rule.style.cssText = value;
        }
      }
    }
  }
}

/* Remove rule from supplied sheet
*/
function removeRule(sheet, rule) {

  // W3C model
  if (typeof sheet.deleteRule == 'function') {
    sheet.deleteRule(rule);

  // IE model
  } else if (sheet.removeRule) {
    sheet.removeRule(rule);
  }
}

/* Add rule from supplied sheet
** Rule is added as last rule in sheet
*/
function addRule(sheet, selectorText, value) {

  // W3C model
  if (typeof sheet.insertRule == 'function') {
    sheet.insertRule(selectorText + ' {' + value + '}', sheet.cssRules.length);

  // IE model
  } else if (sheet.addRule) {
    sheet.addRule(selectorText, value, sheet.rules.length);
  }
}

To animate a change, call the function with the same selector but different values using setTimeout.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • I believe `deleteRule` (and possibly also `removeRule`) take an integer index (into the rule list) as argument, rather than a reference to the rule from the list. As it is written, the reference to the rule being passed in becomes misinterpreted; my tests indicate the browser simply throws out the first rule in the sheet instead of the one being replaced. – Steven Lu Oct 05 '11 at 10:21
  • dammit! using this method in a timer causes very bad problems in chrome. Opera handles it fine. But in Chrome, there is severe overall sluggishness and problems with the select menus. – Steven Lu Oct 05 '11 at 10:32
0

JQuery .animate() should do what you want and it's cross browser.

BHS
  • 1,079
  • 1
  • 6
  • 10
  • I was wondering how long it would take before someone suggests jQuery. I agree that jQuery makes life easier, but the question was about vanilla JavaScript. – JJJ Oct 05 '11 at 06:23
  • Are you sure that jQuery animate modifies CSS rules? I think you'll find it modifies the actual element style properties. – RobG Oct 05 '11 at 06:29
  • It wasn't clear to me that the question was specifically about vanilla JavaScript. I'm still new, when a question is tagged "Javascript" does that imply "vanilla Javascript only"? – BHS Oct 05 '11 at 06:49
  • jQuery can select all elements with a particular class, which would have the same effect. Is it more efficient to change the class properties instead of the properties for all the elements with the class? – BHS Oct 05 '11 at 06:50
  • I'm gonna use jQuery on my next project :) Just wrapping up this one. – Steven Lu Oct 05 '11 at 07:14