48

Is it possible to alter a CSS stylesheet using JavaScript?

I am NOT talking about:

document.getElementById('id').style._____='.....';

I AM talking about altering:

#id {
    param: value;
}

besides doing something dirty (which we haven’t tried yet btw), like creating a new object in the head, innerHTML a style tag in there, etc. Although this, even if it did work, would pose a few issues as the style block is already defined elsewhere, and I’m not sure when/if the browser would even parse a dynamically created style block?

anonymous-one
  • 13,444
  • 18
  • 56
  • 81
  • It’s probably worth trying creating a ` – Paul D. Waite Jul 08 '11 at 09:23

5 Answers5

41

As of 2011

Yes you can, but you will be facing cross-browser compatibility issues:

http://www.quirksmode.org/dom/changess.html

As of 2016

Browser support has improved a lot (every browser is supported, including IE9+).

  • The insertRule() method allows dynamic addition of rules to a stylesheet.

  • With deleteRule(), you can remove existing rules from a stylesheet.

  • Rules within a stylesheet can be accessed via the cssRules attributes of a stylesheet.

Mathieu Rodic
  • 6,459
  • 2
  • 41
  • 49
  • 2
    hate how it makes me wait 11 minutes to correct answer this. this is exactly what i am looking for. thanks. – anonymous-one Jul 08 '11 at 05:49
  • 2
    Be careful, that page is 4 years old, quite a bit has changed since then. e.g. in Firefox 5, the `selectors of the rules according to your browser` shows `0 = body`. – RobG Jul 08 '11 at 06:01
  • 1
    Link now appears to be broken... can anyone edit this answer with a summary of what was once found at the provided link? (Note: I'm at work, so it's possible that the site is just blocked, but either way the content should be posted here if possible). – vastlysuperiorman Sep 29 '15 at 21:49
  • The website does not appear broken to me... the proxy at your workplace might be blocking it. However, have you heard of the [Wayback Machine](https://archive.org/web/)? It allows you to visit a website as it was at a given ponit of time (and *maybe* it is not blocked by the proxy). – Mathieu Rodic Sep 30 '15 at 08:04
  • 1
    @MathieuRodic valid. Does appear to be a problem with my work network. I do still think it's a good idea to include at least summary level detail in the answer for other people who may not have access to the link, but that's just my opinion. – vastlysuperiorman Sep 30 '15 at 15:57
  • 1
    This is no longer true. See [my answer](http://stackoverflow.com/a/38133146/2065702) – Zach Saucier Jun 30 '16 at 21:03
16

We can use a combination of .insertRule and .cssRules to be able to do this all the way back to IE9:

function changeStylesheetRule(stylesheet, selector, property, value) {
    // Make the strings lowercase
    selector = selector.toLowerCase();
    property = property.toLowerCase();
    value = value.toLowerCase();
    
    // Change it if it exists
    for(var i = 0; i < stylesheet.cssRules.length; i++) {
        var rule = stylesheet.cssRules[i];
        if(rule.selectorText === selector) {
            rule.style[property] = value;
            return;
        }
    }
  
    // Add it if it does not
    stylesheet.insertRule(selector + " { " + property + ": " + value + "; }", 0);
}

// Used like so:
changeStylesheetRule(s, "body", "color", "rebeccapurple");

Demo

SimPHP
  • 21
  • 1
  • 7
Zach Saucier
  • 23,662
  • 12
  • 81
  • 137
  • If you're having trouble with old browsers, changing `.cssRules` to `.rules` may help. You can also leave out the second parameter of `.insertRule` for Chrome, but other browsers like FF require it – Zach Saucier Jun 30 '16 at 21:28
  • 1
    If your browser does not update when modifying a style, you can instead delete the style before re-adding it. Do this by changing the `for` loop and adding `s.deleteRule(i)` and removing the `return` statement. – u01jmg3 Sep 25 '16 at 08:55
  • 2
    Also worth noting that due to the lowercasing, attributes such as `backgroundPosition` will not work. This will also need to be converted to its hyphenated equivalent (`background-position`) for use with `insertRule()`. – u01jmg3 Sep 25 '16 at 09:59
  • This doesn't work as OP intended. The edited rule is assigned to a – Banderi Jan 14 '19 at 18:20
4

2020

Some advantages of this method:

  • Does not require (but allows) stylesheet to be specified.
  • Allows multiple styles to be added / modified at once
  • Accepts !important attribute
  • Ignores extra whitespace when matching CSS selector
  • Changes last matching existing rule, or appends to end of last matching stylesheet. (Other answers add/change the first rule which may be then overruled.)

Usage:

adjustCSSRules('#myDiv', 'width: 300px !important');

Method:

function adjustCSSRules(selector, props, sheets){

    // get stylesheet(s)
    if (!sheets) sheets = [...document.styleSheets];
    else if (sheets.sup){    // sheets is a string
        let absoluteURL = new URL(sheets, document.baseURI).href;
        sheets = [...document.styleSheets].filter(i => i.href == absoluteURL);
        }
    else sheets = [sheets];  // sheets is a stylesheet

    // CSS (& HTML) reduce spaces in selector to one.
    selector = selector.replace(/\s+/g, ' ');
    const findRule = s => [...s.cssRules].reverse().find(i => i.selectorText == selector)
    let rule = sheets.map(findRule).filter(i=>i).pop()

    const propsArr = props.sup
        ? props.split(/\s*;\s*/).map(i => i.split(/\s*:\s*/)) // from string
        : Object.entries(props);                              // from Object

    if (rule) for (let [prop, val] of propsArr){
        // rule.style[prop] = val; is against the spec, and does not support !important.
        rule.style.setProperty(prop, ...val.split(/ *!(?=important)/));
        }
    else {
        sheet = sheets.pop();
        if (!props.sup) props = propsArr.reduce((str, [k, v]) => `${str}; ${k}: ${v}`, '');
        sheet.insertRule(`${selector} { ${props} }`, sheet.cssRules.length);
        }
    }

Demo

The method takes three arguments:

  • selector [String] - CSS selector - eg: '#myDiv'
    Whitespaces are auto-reduced (.myClass #myDiv will match .myClass #myDiv)
  • rules [CSS String, Object] - eg (either is acceptable):
    • { border: "solid 3px green", color: "white" }
    • 'border: solid 3px green; color: white'
  • sheet (Optional) [String, StyleSheet]
    • if empty, all stylesheets will be checked
    • 'myStyles.css' A relative or absolute URL to sheet
    • document.styleSheets[1] - A reference to a sheet

Other examples:

adjustCSSRules('#myDiv', {width: '30px'}); // all stylesheets
adjustCSSRules('#myDiv', 'width: 30px', 'style.css'); // style.css only  
adjustCSSRules('#myDiv  .myClass', 'width: 30px', document.styleSheets[0]); // only first stylesheet
SamGoody
  • 12,774
  • 8
  • 77
  • 88
4

When I want to programmatically add a bunch of styles to an object, I find it easier to programmatically add a class to the object (such class has styles asscociated with it in your CSS). You can control the precedence order in your CSS so the new styles from the new class can override things you had previously. This is generally much easier than modifying a stylesheet directly and works perfectly cross-browser.

jfriend00
  • 637,040
  • 88
  • 896
  • 906
  • this is a great idea. we'll be testing various ways of doing things tonight and will get back with what worked best for us :) – anonymous-one Jul 08 '11 at 10:59
2

change a property in a style rule

function change_css_style (titulo,selector,propiedad,valor) {        
        let i=0;
        while (i<document.styleSheets.length) {
            if (document.styleSheets[i].title==titulo) {
                let y=0;
                while (y<document.styleSheets[i].cssRules.length) {
                    if (document.styleSheets[i].cssRules[y].selectorText==selector) {                                               
                        document.styleSheets[i].cssRules[y].style[propiedad] = valor;                                                                       
                        y = document.styleSheets[i].cssRules.length;
                    } 
                    y++;
                }               
                i=document.styleSheets.length;
            } 
            i++;
        }

    }

DEMO

<style title="chat_inicio">
    .contenido .mensajes {
          width: 100px;
          height: 300px;    
    }
</style>

change the style book with the title chat_inicio with the selector .contenido .mensajes the property of the style width to 475px

<script>
     cambiar_css_style ('chat_inicio','.contenido .mensajes','width','475px');
</script>
pedro casas
  • 127
  • 5