Is it possible to alter a CSS stylesheet using JavaScript? (NOT the style of an object, but the stylesheet itself)
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.
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
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.
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>
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