74

Is it possible to make changes to a CSS rule-set dynamically (i.e. some JS which would change a CSS rule-set when the user clicks a widget)

This particular CSS rule-set is applied to lots of elements (via a class selector) on the page and I want to modify it when the user clicks the widget, so that all the elements having the class change.

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
KJ Saxena
  • 21,452
  • 24
  • 81
  • 109
  • 3
    Looking at some of the answers, there seems to be some confusion about what your question means. This isn't helped by the fact that "a CSS class" doesn't exist, but two different things are commonly misdescribed that way. One is "An HTML class", but I think you refer to the other — a CSS rule-set (in this case, with a class selector). – Quentin Sep 11 '09 at 06:46
  • I meant a CSS rule set for a class selector – KJ Saxena Sep 11 '09 at 08:08
  • related - [Setting CSS pseudo-class rules from JavaScript](https://stackoverflow.com/q/311052/104380) – vsync Jul 05 '18 at 09:52

9 Answers9

36

You can, but it's rather cumbersome. The best reference on how to do it is the following article: Totally Pwn CSS with Javascript (web archive link).

I managed to get it to work with Firefox and IE - I couldn't in Chrome, though it appears that it supports the DOM methods.ricosrealm reports that it works in Chrome, too.

Community
  • 1
  • 1
Alex Gyoshev
  • 11,929
  • 4
  • 44
  • 74
  • That's pretty cool! I had no idea you could do that stuff. Looks pretty advanced, though. And I can't really see a good use for it if you got a good framework like Prototype or jQuery, that will do most of that work for you...But still nice to know. Bookmarked for future reference (: +1 – peirix Sep 11 '09 at 06:20
  • Here's a good use: http://stylebuilder.telerik.com/ :P And yes, it's advanced - using jQuery is probably much more suitable for the situation ;) – Alex Gyoshev Sep 11 '09 at 06:26
  • 1
    Here's some references from MDC: https://developer.mozilla.org/en/DOM/document.styleSheets https://developer.mozilla.org/En/DOM/Stylesheet – Fábio Apr 16 '10 at 12:19
  • 3
    Tested in Chrome 11. Works great. – ricosrealm Jun 23 '11 at 21:29
  • 2
    That guide is AWESOME!!! I've tested it on latest Firefox and Chrome and it works like a charm!!! – lucaferrario Jun 06 '12 at 01:02
  • 2
    The link is broken (database down), but it looks like [someone turned it into a library](http://jelo.callee.info/docs/symbols/Jelo.CSS.html#.deleteRule). – David Harkness Mar 24 '16 at 19:10
  • 1
    This answer is outdated. See a modern solution [here](http://stackoverflow.com/a/38133146/2065702) – Zach Saucier Jun 30 '16 at 21:07
  • This answer is pretty much a link-only answer. and doesn't answer the question on-site. – Heretic Monkey Jul 03 '18 at 12:48
14

This is a modern version based on Totally Pwn CSS with Javascript. It's ES6 I hope don't mind.

function getCSSRule(ruleName) {
    ruleName = ruleName.toLowerCase();
    var result = null;
    var find = Array.prototype.find;

    find.call(document.styleSheets, styleSheet => {
        result = find.call(styleSheet.cssRules, cssRule => {
            return cssRule instanceof CSSStyleRule 
                && cssRule.selectorText.toLowerCase() == ruleName;
        });
        return result != null;
    });
    return result;
}

This function returns a CSSStyleRule that you can use like this:

var header = getCSSRule('#header');
header.style.backgroundColor = 'red';

Also document.styleSheets list references of the CSSStylesSheets Objects. Other way to acces a specific sytleSheet in the page is by assigning an id to the style or link element in the html code, and get it in javascript using document.getElementById('my-style').sheet. This are some useful methods:

Major Browsers and IE9+ : insertRule(), deleteRule(), removeProperty().

Major Browsers, Firefox? and IE9+ : setProperty().

<stye id="my-style" ...
....
var myStyle = document.getElementById('my-style').sheet
myStyle.insertRule('#header { background: red; }', 0);

It is also possible to dynamically create a new style element to store dynamic created styles, I think should be way to avoid conflicts.

Jorge Gonzalez
  • 189
  • 1
  • 5
8

You can edit CLASS in document styleshets as follows

[...document.styleSheets[0].cssRules].find(x=> x.selectorText=='.box')
     .style.background= 'red';

function edit() {
  [...document.styleSheets[0].cssRules].find(x=> x.selectorText=='.box')
    .style.background= 'red';
}
.box {
  margin: 10px;
  padding: 10px;
  background: yellow;
}
<button onclick="edit()" >Click me</button>
<div class="box" >My box 1</div>
<div class="box" >My box 2</div>
<div class="box" >My box 3</div>
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
4

Depending on what you're trying to achieve, a better solution might be to change/add a class to a containing element (body would do!), and define classes accordingly.

.yourclass { color: black }
#wrapper.foo .yourclass { color: red }
#wrapper.bar .yourclass { color: blue }

then you can just use

document.getElementById('wrapper').className='foo';

(or your chosen js framework's wrapper for the same) to change everything with class yourclass inside whatever your wrapper element is.

John Hascall
  • 9,176
  • 6
  • 48
  • 72
Pete Jordan
  • 558
  • 4
  • 2
  • 1
    In response to your parenthetical: yes, there's a way to specify code languages explicitly (http://meta.stackexchange.com/questions/63800/interface-options-for-specifying-language-prettify) – Brilliand Feb 14 '14 at 21:33
4

I tried the code via link from @alex-gyoshev comment, but it dosn't work

  1. it fails on the CSS rules with Google fonts in Chrome
  2. it fails on FireFox security checks

So I changed it slightly, but deleted delete functionality since it wasn't needed for me. Checked in IE 11, FireFox 32, Chrome 37 and Opera 26.

function getCSSRule(ruleName) { // Return requested style object
  ruleName = ruleName.toLowerCase(); // Convert test string to lower case.
  var styleSheet;
  var i, ii;
  var cssRule = false; // Initialize cssRule. 
  var cssRules;
  if (document.styleSheets) { // If browser can play with stylesheets
    for (i = 0; i < document.styleSheets.length; i++) { // For each stylesheet
      styleSheet = document.styleSheets[i];
      if (!styleSheet.href) {
        if (styleSheet.cssRules) { // Browser uses cssRules?
          cssRules = styleSheet.cssRules; // Yes --Mozilla Style
        } else { // Browser usses rules?
          cssRules = styleSheet.rules; // Yes IE style. 
        } // End IE check.
        if (cssRules) {
          for (ii = 0; ii < cssRules.length; ii++) {
            cssRule = cssRules[ii];
            if (cssRule) { // If we found a rule...
              // console.log(cssRule);
              if (cssRule.selectorText) {
                console.log(cssRule.selectorText);
                if (cssRule.selectorText.toLowerCase() == ruleName) { //  match ruleName?
                  return cssRule; // return the style object.
                }
              }
            }
          }
        }
      }
    }
  }
  return false; // we found NOTHING!
}
mseifert
  • 5,390
  • 9
  • 38
  • 100
ornic
  • 332
  • 3
  • 9
  • the css rules are case insensitives? – sarkiroka Jan 18 '15 at 14:38
  • 1
    https://www.google.ru/search?q=CSS+case+sensitive -> http://www.w3.org/TR/CSS2/syndata.html#characters "All CSS syntax is case-insensitive within the ASCII range (i.e., [a-z] and [A-Z] are equivalent), except for parts that are not under the control of CSS." – ornic Jan 18 '15 at 19:11
3

The APIs for editing stylesheets with JS are, sadly, not consistent across browsers. The YUI Stylesheet Utility attempts to smooth over these differences so you could just use that. You could also look at the source code to figure out how it works if you don't want to use YUI itself.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
3

give your style tag an id, like <style id="ssID"> if someonelse is making your styles for you tell THAT person to give the style tag an id - that way you can access it directly without scrambling around wondering what its index is

// create a hash table
var cssHash = {};

// loop through and populate the hash table
for (let i in (r = ss0.sheet.rules)) {

    // selectorText is the name of the rule - set the value equal to the rule
    cssHash[r[i].selectorText] = r[i];

}

now you have a hash table for everything in the style sheet - note that some values will be undefined, but not for any of the things you care about

if you have, for instance, a class called #menuItem and you want to change its color to black, do this

cssHash['#menuItem'].style.color = #000;

that line will set the color of the style of the rule whose index was looked up in the hash table (cssHash) by the name '#menuItem'

more importantly, you probably have several different classes that you want to change all at once kind of like when you switched majors in college

let's say you have four different classes and you want to set all of their background colors to the same value, that some user selected from an input

the color selector tag is <input id="bColor" type="color"> and the class rules you want to change are called #menuItem .homeAddr span and #vacuum:hover

// create a listener for that color selector
bColor.addEventListener('input', function (e) {

  // loop through a split list of the four class names
  '#menuItem .homeAddr span #vacuum:hover'.split(' ').forEach(function (obj) {

    // use the hash table to look up the index of each name
    // and set the background color equal to the color input's value
    cssHash[obj].style.backgroundColor = bColor.value;

  });

}, false); // false added here for the sake of non-brevity
stoke motor
  • 101
  • 2
  • Just passing through, but most of the time people here post the explanation above their code, rather than as comments mixed in with the code. You might want to consider doing that in the future. – Peter G Apr 22 '17 at 06:10
  • This is quite a useful idea, but keep in mind that the ss0.sheet.rules can potentially have an element with key "length" and might even have "item" depending on browser etc. – Asu May 16 '17 at 13:00
2

To check all stylesheets for the rule and set it:

Your rule:

.aaa: {
  background-color: green
}
[...document.styleSheets].flatMap(s=>[...s.cssRules])
  .find(i=>i.selectorText=='.aaa').style.backgroundColor = 'red';

Note that the css styles, when accessed through javascript, do not have dashes in them. In the example above, background-color becomes backgroundColor

Andrew Parks
  • 6,358
  • 2
  • 12
  • 27
0

While setAttribute is nice, there is a standard way of doing this across most browsers:

htmlElement.className = 'someClass';

To do it over many elements, you will need a cross browser solution:

function getElementsByClassName( className, context, tagName ) {
  context = context || document;
  if ( typeof context.getElementsByClassName === 'function' )
    return context.getElementsByClassName( className );

  if ( typeof context.getElementsByTagName !== 'function' )
    return [];

  var elements = typeof tagName === 'string' ? context.getElementsByTagName( tagName ) :
    context.getElementsByTagName('*'),
  ret = [];
  for ( var i = 0, il = elements.length; i < il; i++ )
    if ( elements[ i ].className.match( className ) )
      ret.push( elements[ i ] );

  return ret;
}

var elements = getElementsByClassName('someClass');

for ( var i = 0, il = elements.length; i < il; i++ )
  elements[ i ].className = 'newClass';

You may want to replace the line:

if ( elements[ i ].className.match( className ) )

With some Regular Expression, but you will have to escape special characters in that case.

Tim
  • 2,051
  • 2
  • 14
  • 9
  • 1
    Regarding the last part of your answer, I think something other than match should be used, to account for multiple class names, in any order. For example, "foo bar".match("foo bar") will return "foo bar", but "foo bar".match("bar foo") will return null. Perhaps splitting by spaces and checking all class names in a for loop would work (however, leading/trailing white space should be ignored, and care taken not to get an empty string in array from an extra space somewhere). Reference: [W3 Semantics](http://www.w3.org/TR/html5/dom.html#dom-document-getelementsbyclassname) – Chase Feb 06 '12 at 06:13