Is it possible to alter a CSS stylesheet using JavaScript? (NOT the style of an object, but the stylesheet itself)

Viewed 32252

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?

5 Answers

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

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>
Related