Manipulating CSS with JavaScript - javascript

I would like to use JavaScript to manipulate my CSS. First it was just thought to be a nice little script to try out different colors for my accordion menu together with different backgrounds/title-/content-/... background-colors from an input field.
I understand how I get the input value with js.
I understand how CSS is manipulated by using getElementById(), getElementsByClassName(), getElementsByTag(), and getElementsByName().
Now, the problem is that my CSS looks like this:
.accordion li > a {
/* some css here */
}
.sub-menu li a {
/* some css here */
}
.some-class hover:a {
/* css */
}
.some-other-class > li > a.active {
/* css */
}
How would I change the properties of such stylings with JavaScript?

There's no way to manipulate some CSS styles directly with JavaScript. Instead you can change a rule in a stylesheet itself, something like this:
var changeRule = function(selector, property, value) {
var styles = document.styleSheets,
n, sheet, rules, m, done = false;
selector = selector.toLowerCase();
for(n = 0; n < styles.length; n++) {
sheet = styles[n];
rules = sheet.cssRules || sheet.rules;
for(m = 0; m < rules.length; m++) {
if (rules[m].selectorText.toLowerCase() === selector) {
done = true;
rules[m].style[property] = value;
break;
}
}
if (done) {
break;
}
}
};
changeRule('div:hover', 'background', '#0f0');
selector must match exactly an exisiting selector, only spaces between selector text and { are ignored.
You can develope the code to find and change partial hits of selector names, or just check a particular stylesheet instead of all of them. As it is, it's also quite expensive when having tens of stylesheets with thousands of rules.
Unfortenately pseudo elements can't be manipulated with this snippet.
A live demo at jsFiddle.

All DOM elements have a style object that can be altered by JavaScript
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.style?redirectlocale=en-US&redirectslug=Web%2FAPI%2Felement.style
Or if you're using jQuery:
http://api.jquery.com/css/
You can target elements and manipulate their propertoes, but you do not alter the rules.
A common approach if you want to alter large numbers of style properties is to alter elements' class names to change their appearance. This can be done with the className property, or if you're using jQuery: addClass and removeClass.

I've implemented Teemu's answer with underscore. http://jsfiddle.net/6pj3g/4/
var rule = _.chain(document.styleSheets)
.map(function(sheet){return _.flatten(sheet.cssRules)})
.flatten()
.unique()
.find(function(rule){ return rule && rule.selectorText && (rule.selectorText.toLowerCase() === selector.toLowerCase())})
.value()
if (rule){
rule.style[property] = value;
} else {
throw 'selector not found: ' + selector;
}

Related

Set HTML stylesheet from JavaScript [duplicate]

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.
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.
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.
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>
I tried the code via link from #alex-gyoshev comment, but it dosn't work
it fails on the CSS rules with Google fonts in Chrome
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!
}
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.
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.
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
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.
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

Altering Stylesheet with javascript? [duplicate]

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.
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.
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.
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>
I tried the code via link from #alex-gyoshev comment, but it dosn't work
it fails on the CSS rules with Google fonts in Chrome
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!
}
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.
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.
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
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.
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

Change styling of a psuedo after element with javascript

Is it possible to change the styling of a psuedo :after element with javascript something like this:
document.querySelector('#test:after').attr("style", "content:url('blabla.png')");
Is there any workaround to change the image after 1 click like this:
var timesClicked = 0;
span = document.querySelector('.socialShare');
span.addEventListener('click', function (e) {
timesClicked++;
if (timesClicked > 1) {
document.querySelector('#socialShare').style.left = '-60px';
timesClicked = 0;
console.log(timesClicked)
} else {
document.querySelector('#socialShare').style.left = '0';
document.querySelector('#socialShare:after').attr("style", "content:url('blabla.png')");
console.log(timesClicked)
}
});
Or maybe better transform the image it is about an arrow which needs to point the other way when div is expanded
You can't change styles of pseudo elements with javascript, because they are not part of the DOM, and so do not have any API to work with.
Usual approach is to change classes of the element itself and have those classes affect related pseudo elements. For example in your case:
// Add class selected to element
document.querySelector('#test').classList.add('selected')
In CSS:
#test.selected::after {
content: url('blabla.png');
}

Editing css styles with jquery [duplicate]

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.
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.
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.
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>
I tried the code via link from #alex-gyoshev comment, but it dosn't work
it fails on the CSS rules with Google fonts in Chrome
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!
}
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.
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.
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
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.
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

Changing a CSS rule-set from Javascript

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.
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.
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.
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>
I tried the code via link from #alex-gyoshev comment, but it dosn't work
it fails on the CSS rules with Google fonts in Chrome
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!
}
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.
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.
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
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.
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

Categories