27

Is there a way to change an attribute of a CSS class using javascript?

<style type="text/css">
  .fool select {
    display: block;
  }
</style>

<p class="fool">
  <select id="a" onchange="changeCSS()"> ... </select>
  <select id="b" > ... </select>
  <select id="c" > ... </select>
</p>

I want to change display:block to display:none for ALL <select> elements after a user call function changeCSS().

It looks simple but I can't find a way to do this...

icyrock.com
  • 27,952
  • 4
  • 66
  • 85
Bedo
  • 925
  • 2
  • 14
  • 27
  • Possible duplicate of http://stackoverflow.com/questions/622122/how-can-i-change-the-css-class-rules-using-jquery – j08691 Feb 05 '12 at 22:36
  • 9
    @j08691: No, that question is about **jQuery**. This one doesn't pre-suppose your use of that library. – T.J. Crowder Feb 05 '12 at 22:46
  • You may look at this site http://quirksmode.org/dom/w3c_css.html – Michas Feb 05 '12 at 23:05
  • The [default display property](http://www.w3.org/TR/CSS21/sample.html) for a select element is `inline-block`, not `block`. You are much better off to toggle between `none` and `''` (empty string) so that the element adopts its default display property when not set to `none`. – RobG Feb 06 '12 at 03:02
  • 1
    Yes but I specifically need "block" as attribute :-) – Bedo Feb 06 '12 at 19:22

8 Answers8

24

You can modify style rules, but it's usually not the best design decision.

To access the style rules defined by style sheets, you access the document.styleSheets collection. Each entry in that collection will have a property either called cssRules or rules depending on the browser. Each of those will be a CSSRule instance. You can change the rule by changing its cssText property.

But again, that's probably not the best way to solve the problem. But it is the literal answer to your question.

The best way to solve the problem is probably to have another class in your stylesheet that overrides the settings of the previous rule, and then to add that class either to the select elements or to the container of them. So for instance, you could have the rules:

.fool select {
    display: block;
}
.fool.bar select {
    display: none;
}

...and when you want to hide the selects, add the "bar" class to the container that has the "fool" class.

Alternately, apply CSS style information directly to elements.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 5
    Modifying rules makes sense where there are a large number of elements with the class to change, it is hugely faster than changing the elements one by one (which is what any selector based function must do). Delegating changes to a parent is pretty fast too. – RobG Feb 06 '12 at 03:06
  • 1
    Perfect good answer - you explain, that it is mostly not the best decision, but instead of trying to forbid something on philosophical reasons, you rather explain, how to do this if it would be needed. An example, as it could be useful: you have a lot of objects in the DOM, and modifying the class (and let the hard counting work to the C++ code of the browser) results a better app, as looping the elements by class selector (and doing the hard work from JS). – peterh Mar 20 '17 at 14:05
  • @peterh: Yeah, although the last time I tried this (a few weeks back, trying to answer a different question), it seemed like rules had become read-only. I never had time to follow up on that... – T.J. Crowder Mar 20 '17 at 15:01
18

The key is to define extra rules for additional classes and add these classes to the elements rather than to rewrite the rules for a given style rule.

JS

function changeCSS() {
  var selects = document.getElementsByTagName("select");
  for(var i =0, il = selects.length;i<il;i++){
     selects[i].className += " hidden";
  }
}

CSS

.fool select.hidden, select.hidden {
   display: none;
}

Or for a really efficient method (but which might need a few more specific style rules too)

JS

function changeCSS() {
  document.getElementsByTagName("body")[0].className += " hideAllSelects"
}

CSS

body.hideAllSelects select {
   display: none;
}
wheresrhys
  • 22,558
  • 19
  • 94
  • 162
  • 1
    Thank you! This works for me: var selects = document.getElementsByTagName("select"); for(var i =0, il = selects.length;i – Bedo Feb 05 '12 at 23:16
  • instead of i.e. `.className += " hidden"` you can use `.classList.Add("hidden")`. It is a little bit more performant and better maintainable. – Martin Schneider Apr 10 '18 at 11:05
15

I'm accessing CSS classes directly to adjust the height of a bunch of divs simultaneously. This is how I'm doing it:

function cssrules() {
    var rules = {};
    for (var i=0; i<document.styleSheets.length; ++i) {
        var cssRules = document.styleSheets[i].cssRules;
        for (var j=0; j<cssRules.length; ++j)
            rules[cssRules[j].selectorText] = cssRules[j];
    }
    return rules;
}

function css_getclass(name) {
    var rules = cssrules();
    if (!rules.hasOwnProperty(name))
        throw 'TODO: deal_with_notfound_case';
    return rules[name];
}

and then you can do something like css_getclass('.classname').style.background="blue". I only tried this on chrome for windows, good luck with other browsers.

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
amwinter
  • 3,121
  • 2
  • 27
  • 25
  • 1
    This worked fine in firefox 21, opera 12.12 and ie 10 as well. More elaborate selector rules also work. `css_getclass('td:first-child a img').style.width=this.value+"em";` – Stephen Jul 12 '13 at 10:20
  • 1
    Note by compatibility you need change `dsi=ds[i].cssRules` to `dsi=ds[i].cssRules||ds[i].rules` and be aware that the funcion will overwritte rules if the same selectorText exists in a previous styleSheet. – aMarCruz Feb 14 '16 at 00:23
  • I'm not sure if that's the reason, but it seems to me that this only works if the class exists in a separate stylesheet file (and not in the html tag) and it doesn't seem to work with specifics like `kbd.myclass`. – lucidbrot Jul 28 '18 at 15:36
  • Also, it's important to note that as of chrome 64, [this won't work locally anymore](https://stackoverflow.com/a/49160760/2550406) but it seems to still be working in edge 40 – lucidbrot Jul 28 '18 at 15:41
3

Here's how I'd do it:

//Broken up into multiple lines for your convenience
setTimeout
(
  function()
  {
    document.getElementById('style').innerHTML = 'p.myclass{display: none}';
  }, 5000
);
<!--Should this be separated into "<head>"?-->
<style id="style"></style>

<p>After 5 seconds:</p>
<p>This text won't be hidden,</p>
<p class="myclass">But this will,</p>
<p class="myclass">And so will this.</p>

<script>
 style = document.getElementById('style');
 setTimeout(function(){style.innerHTML = 'p.myclass{display: none}'}, 5000);
</script>
FluorescentGreen5
  • 879
  • 11
  • 23
3

You can edit the style sheets, but I would think you would just create a new class instead. If you really need to do it, try this.

    ss=document.styleSheets[0]

cssRules Are the style sheet rules

    ss.cssRules
    ss.cssRules[0]

Selector for the first rule

    ss.cssRules[0].selectorText

    ".channel > li"

Read the style of the first rule

    ss.cssRules[0].style

    CSS2Properties { "margin-left" → "0px"}

Change the style

    ss.cssRules[0].style.marginLeft="15px"

    ss.cssRules[0].style

CSS2Properties { "margin-left" → "15px" }

Leroy Scandal
  • 329
  • 1
  • 4
1

To get the same effect, you can make another style:

<style type="text/css">
  .fool select {
    display: block;
  }
  .foolnone select {
    display: none;
  }
</style>

and change the class of <p> to foolnone.

Otherwise, you'd have to go through each of the children of <p> and change the class. If that's the way you want to go, probably probably best to use some library, such as jquery. With it, you can do something like:

<style>
.fool select.displaynone {
   display: none
}

$('.fool>select').toggleClass('displaynone')

See this jsfiddle for a working example:

This shows both above approaches (i.e. hiding the whole <p> and hiding each of the <select>s.

icyrock.com
  • 27,952
  • 4
  • 66
  • 85
  • I'd always prefer to add another class, and define a different style rule using a longer selector, rather than changing the class as the existing class "fool" could be used for other things too and it might not be safe to remove it entirely – wheresrhys Feb 05 '12 at 22:49
  • @wheresrhys Completely agree - see the example I added, showing both approaches. It adds `displaynone` class for this. However that fits into your project is something you can ponder on, but this should be what you can use basically, details aside. – icyrock.com Feb 05 '12 at 22:53
0

I think it would work if instead put like this:

var elements = document.getElementsByTagName('select');
for(var i = 0; i < elements.length; i++){
    elements[i].style.display = 'none';
}
Rott
  • 1
-1
 for(var elements = document.getElementsByTagName('select'), i = elements.length; i--)
     elements[i].style.display = "none";
Gabe
  • 49,577
  • 28
  • 142
  • 181
  • Doesn't work (even replacing "," with ";" before "i = ..." This is the error: "Uncaught TypeError: Cannot read property 'style' of undefine" – Bedo Feb 05 '12 at 22:54