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
Related
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
I would like to change the styling attribute values of all elements that have the class "post-feature" and contain an attribute value of "http"
So the div element will look like the following:
<div class="post-feature" style="backgroundimage:url(http://local.test.com/test_image.jpg);">
So far the http check works. But I am not able to set the attribute value.
I have the following code
var features = document.getElementsByClassName(".post-feature")
[0].getAttribute("style");
if (features.includes("http")) {
features.setAttribute("background-color", "orange");
} else {
alert('no change');
}
You can use querySelectorAll('.post-feature[style*="http"]') to find those elements.
Then simply iterate through them and i.e. set their background color with
element.style.backgroundColor = 'orange';
Now, if you want to make sure you only target elements having a background-image and http, you can use this selector:
querySelectorAll('.post-feature[style*="http"][style*="background-image"]')
Also, by adding an i (or I) just before the end bracket [style*="http"i], the value will be compared case-insensitively.
window.addEventListener('load', function() {
var elements = document.querySelectorAll('.post-feature[style*="http"]');
for (var i = 0; i < elements.length; i++) {
elements[i].style.backgroundColor = 'orange'; /* add propert value */
/* replace class
elements[i].className = 'myClass';
*/
/* add a class
elements[i].classList.add('myClass');
*/
}
/* temp log */
console.log('Found ', elements.length,' element(s)');
})
div {
height: 40px;
background-color: gray;
}
div + div {
margin-top: 10px;
}
<div class="post-feature" style="background-image:url(http://local.test.com/test_image.jpg);"></div>
<div class="post-feature"></div>
<div class="post-feature" style="background-image:url(http://local.test.com/test_image.jpg);"></div>
<div class="post-feature"></div>
Updated
To only change styling, like colors etc., you don't even need a script, you can use CSS alone
div {
height: 40px;
background-color: gray;
}
div + div {
margin-top: 10px;
}
/* for elements that contain "http" and "background-image" */
.post-feature[style*="http"i][style*="background-image"i] {
background-color: orange;
}
<div class="post-feature" style="background-image:url(http://local.test.com/test_image.jpg);"></div>
<div class="post-feature"></div>
<div class="post-feature" style="background-image:url(HTTP://local.test.com/test_image.jpg);"></div>
<div class="post-feature"></div>
As a note, and as discussed in a few comments, if to make sure it is the background-image property that also contain the http in its url(), you can adjust the selector to this, which as well can be used without any script, as a CSS rule
.post-feature[style*="background-image:url(http"i] {
background-color: orange;
}
The above selector can of course also be used in the first sample, like this
querySelectorAll('.post-feature[style*="background-image:url(http"i]')
First, you can use querySelctorAll() with a CSS query that selects the elements with the class you desire and, in most cases, you should use this instead of getElementsByClassName() as that returns a "live node list" that causes the DOM to be re-scanned every time you access it.
Next, setAttribute() is for setting HTML element attributes. You are asking to change the value of a CSS property. While that could be accomplished with setAttribute('style', value), it is very "old-school" and not the best approach, nor is getAttribute('style') the best way to read a CSS property value (it won't work if the CSS was set from a style sheet).
Also, your code is trying to access: backgroundimage, but the property is accessed as background-image when working in CSS and backgroundImage when accessing it via JavaScript.
To access the inline styles applied to an HTML element, just access the style property of that element, followed by the name of the CSS property you are interested in. For example:
var bColor = element.style.backgroundColor;
If the style has been applied to the element from an internal style sheet or an external style sheet, the above approach won't work for you and you'll need to get it another way, via window.getComputedStyle():
var bColor = window.getComputedStyle(element, null).backgroundColor;
But, note that getComputedStyle() doesn't always return the same value that you set - - it's the value after the browser has computed all factors. In this case, even paths that you wrote as relative references (without the "http") will be returned as absolute paths (with the http).
So, here is a modern approach that correctly checks only the background-image CSS property for the presence of http.
NOTE: This solution tests for http specifically in the background-image property. Unlike most of the other answers given, this code will correctly ignore http in other CSS properties besides background-image. Examine the CSS of the last div to see this in action.
// querySelectorAll() is more efficient than getElementsByClassName()
var features = document.querySelectorAll(".post-feature");
// Loop over the list
for(var i = 0; i < features.length; i++){
// Get access to the background-image property (called backgroundImage from JavaScript) value,
// convert that value to lower case and check to see if "http" is in that value
if(features[i].style.backgroundImage.toLowerCase().indexOf("http") > -1){
// Set the CSS background-color property (called "backgroundColor" in JavaScript) to orange:
features[i].style.backgroundColor = "orange";
// Just for testing:
features[i].textContent = features[i].style.backgroundImage;
} else {
alert("No change");
}
}
.post-feature { width:100%; height:50px; border:1px solid black; background-color:gray; color:yellow; }
<!-- The correct CSS property is "background-image", not "backgroundimage" -->
<div class="post-feature" style="background-image:url(http://local.test.com/test_image.jpg);"></div>
<div class="post-feature" style="background-image:url(test_image.jpg);"></div>
<div class="post-feature" style="background-image:url(http://local.test.com/test_image.jpg);"></div>
<div class="post-feature"
style="border-image: url('http:///images/border.png') 30 30 repeat;background-image:url(test_image.jpg);">I have "http" in one of my CSS properties, but not "background-image", so I shouldn't be orange.</div>
i think some wrong in your code, try this code
element.setAttribute("style", "background-color: orange;"); // bad
or
element.style.backgroundColor = "orange"; // good
Use element.style.backgroundColor and indexOf
var features = document.getElementsByClassName(".post-feature")[0].getAttribute("style");
if (features.indexOf("http") > -1) {
features.style.backgroundColor = "orange";
} else {
alert('no change');
}
check this fiddle
https://jsfiddle.net/vywk72j8/2/
<div class="post-feature" style="background-image:url(http://local.test.com/test_image.jpg);">
tt</div>
var feature = document.getElementsByClassName("post-feature")[0];
if (feature.style.backgroundImage.indexOf("http") !== -1) {
feature.style.backgroundColor = "orange";
} else {
alert('no change');
}
In your code, you are fetching the attribute value in features
var features = document.getElementsByClassName(".post-feature")
[0].getAttribute("style");
Here features is a string containing attribute value, not an element so you cannot use it to set value.
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
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;
}
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