Reputation: 581
I've run into a unique situation that I have so far been unable to find a solution for: dynamically assigning a value to a CSS style. I know how to use jQuery to assign width, height, etc. to an element, but what I'm trying to do is actually change the value defined in the stylesheet so that the dynamically-created value can be assigned to multiple elements.
What I'm building is a slideshow of images that occupy the full viewport, recalculating the image's width, height, and left properties on resize so that the image is always centered, favors width over height, except when the viewport is taller than it is wide (resizing does not reload the page, just fires a function to resize the image).
I have successfully been able to get it to work on one image, and now I'm trying to determine the best way to assign those property values to all images in the slideshow without having to specify those three things individually for every image.
Can the values of properties in a class be modified on the fly? I'm sure the answer is out there, I'm probably just not using the correct terminology in my searches. Hope I did a good job of describing the problem. TIA.
Upvotes: 58
Views: 110101
Reputation: 31320
The way to dynamically modify a CSS property value in a CSS class is by using a CSS variable in the CSS class property value, and then use JavaScript and the DOM to change the CSS variable. The end result is that the styling on the device screen will immediately change when the code runs that sets the new CSS variable value. So, code changes the CSS variable, and the CSS variable is used in the CSS class setting. Note that a CSS class name and a JavaScript Class are two different things.
<style>
:root { /* Set CSS variable values - These values can be changed with JavaScript
and DOM */
--myCSS_ValueOne: initialValue;
}
.classNameHere {
property_Name_Here: var(--myCSS_ValueOne);/* This CSS property gets its value from
the CSS variable */
}
</style>
JavaScript and DOM
<script>
var r = document.querySelector(':root');//Get global root element
function setA_NewCSS_VariableValue() {
r.style.setProperty('--myCSS_ValueOne', 'the_New_Value');// Set a new CSS variable
//value which immediately changes the CSS class setting because the CSS
//property setting uses a CSS Variable -
}
</script>
It is also possible to run a function in response to media queries (changes in the device viewport - like width of the device screen) using the matchMedia()
method.
var x = window.matchMedia("(max-width: 300px)")
x.addListener(setA_NewCSS_VariableValue) // create a listener function that runs
//when the viewport is less than, or equal to, 300 pixels wide.
Upvotes: 5
Reputation: 401
I've got a solution for changing a value in specific CSS class. But it only works if you keep your CSS in the tag. If you just keep a link to your CSS from external files ex.
<style src='script.js'></style>
this solution won't work.
If your css looks like this for example:
<style id='style'>
.foo {
height:50px;
}
</style>
You can change a value of the tag using JS/jQuery.
I've written a function, perhaps it's not the best one but it works. You can improve it if you want.
function replaceClassProp(cl,prop,val){
if(!cl || !prop || !val){console.error('Wrong function arguments');return false;}
// Select style tag value
var tag = '#style';
var style = $(tag).text();
var str = style;
// Find the class you want to change
var n = str.indexOf('.'+cl);
str = str.substr(n,str.length);
n = str.indexOf('}');
str = str.substr(0,n+1);
var before = str;
// Find specific property
n = str.indexOf(prop);
str = str.substr(n,str.length);
n = str.indexOf(';');
str = str.substr(0,n+1);
// Replace the property with values you selected
var after = before.replace(str,prop+':'+val+';');
style=style.replace(before,after);
// Submit changes
$(tag).text(style);
}
Then just change the tag variable into your style tag id and exegute:
replaceClassProp('foo','height','50px');
The difference between this and $('.foo').css('height','50px'); is that when you do it with css method of jQuery, all elements that have .foo class will have visible style='height:50px' in DOM. If you do it my way, elements are untouched and the only thing youll see is class='foo'
Advantages
Disadvantages
Hope it helps anyhow.
Upvotes: 5
Reputation: 94499
You should really rethink your approach to this issue. Using a well crafted selector and attaching the class may be a more elegant solution to this approach. As far as I know you cannot modify external CSS.
Upvotes: 1
Reputation: 1
This solution modifies Cycne's to use ES6 syntax and exit the loop early for external stylesheets. This solution does not modify external stylesheets
function changeStyle(findSelector, newDeclarations) {
// Change original css style declaration.
document.styleSheets.forEach((sheet) => {
if (sheet.href) return;
const cssRulesList = sheet.cssRules;
cssRulesList.forEach((styleRule) => {
if (styleRule.selectorText === findSelector) {
Object.keys(newDeclarations).forEach((cssProp) => {
styleRule.style[cssProp] = newDeclarations[cssProp];
});
}
});
});
}
const styleDeclarations = {
'width': '200px',
'height': '400px',
'color': '#F00'
};
changeStyle('.paintBox', styleDeclarations);
You must also have at least one style tag in your HTML head section, for example
<style> .paintBox {background-color: white;}</style>
Upvotes: 0
Reputation: 31
function changeStyle(findSelector, newRules) {
// Change original css style declaration.
for ( s in document.styleSheets ) {
var CssRulesStyle = document.styleSheets[s].cssRules;
for ( x in CssRulesStyle ) {
if ( CssRulesStyle[x].selectorText == findSelector) {
for ( cssprop in newRules ) {
CssRulesStyle[x].style[cssprop] = newRules[cssprop];
}
return true;
}
}
}
return false;
}
changeStyle('#exact .myStyle .declaration', {'width':'200px', 'height':'400px', 'color':'#F00'});
Upvotes: 3
Reputation:
This may be late to the discussion, but I needed something like what is being talked about here, but didn't find anything that really did what I wanted, and did it easily. What I needed was to hide and show numerous elements without explicitly visiting each element individually to update them in some way that changed the display style to and from hidden. So I came up with the following:
<style>
/* The bulk of the css rules should go here or in an external css file */
/* None of these rules will be changed, but may be overridden */
.aclass { display: inline-block; width: 50px; height: 30px; }
</style>
<style id="style">
/* Only the rules to be changed should go in this style */
.bclass { display: inline-block; }
</style>
<script>
//
// This is a helper function that returns the named style as an object.
// This could also be done in other ways.
//
function setStyle() { return document.getElementById( 'style' ); }
</script>
<div id="d1" class="aclass" style="background-color: green;">
Hi
</div>
<!-- The element to be shown and hidden -->
<div id="d2" class="aclass bclass" style="background-color: yellow;">
there
</div>
<div id="d3" class="aclass" style="background-color: lightblue;">
sailor
</div>
<hr />
<!-- These buttons demonstrate hiding and showing the d3 dive element -->
<button onclick="setStyle().innerHTML = '.bclass { display: none; }';">
Hide
</button>
<button onclick="setStyle().innerHTML = '.bclass { display: inline-block; }';">
Show
</button>
By toggling the bclass rule in the embedded and named stylesheet, which comes after the any other relevant style sheets, in this case one with the aclass rule, I could update the just the display css rule in one place and have it override the aclass rule, which also had it's own display rule.
The beauty of this technique is that it is so simple, effectively one line that does the actual work, it doesn't require any libraries, such as JQuery or plug-ins, and the real work of updating all of the places where the change applies is performed by the browser's css core functionality, not in JavaScript. Also, it works in IE 9 and above, Chrome, Safari, Opera, and all of the other browsers that MicroSoft Edge could emulate, for desktop and tablet/phones devices.
Upvotes: 1
Reputation: 5044
Okay.. had the same problem and fixed it, but the solution may not be for everyone.
If you know the indexes of the style sheet and rule you want to delete, try something like document.styleSheets[1].deleteRule(0);
.
From the start, I had my main.css (index 0) file. Then, I created a new file, js_edit.css (index 1), that only contained one rule with the properties I wanted to remove when the page had finished loading (after a bunch of other JS functions too).
Now, since js_edit.css loads after main.css, you can just insert/delete rules in js_edit.css as you please and they will override the ones in main.css.
var x = document.styleSheets[1];
x.insertRule("p { font-size: 2rem; }", x.cssRules.length);
x.cssRules.length
returns the number of rules in the second (index 1) style sheet thus inserting the new rule at the end.
I'm sure you can use a bunch of for-loops to search for the rule/property you want to modify and then rewrite the whole rule within the same sheet, but I found this way simpler for my needs.
http://www.quirksmode.org/dom/w3c_css.html helped me a lot.
Upvotes: 1
Reputation: 288670
You could use the following function:
function setStyle(cssText) {
var sheet = document.createElement('style');
sheet.type = 'text/css';
/* Optional */ window.customSheet = sheet;
(document.head || document.getElementsByTagName('head')[0]).appendChild(sheet);
return (setStyle = function(cssText, node) {
if(!node || node.parentNode !== sheet)
return sheet.appendChild(document.createTextNode(cssText));
node.nodeValue = cssText;
return node;
})(cssText);
};
Features:
setStyle
, so if you don't call it, it won't create any stylesheet.setStyle
Example
var myCSS = setStyle('*{ color:red; }');
setStyle('*{ color:blue; }', myCSS); // Replaces the previous CSS with this one
Browser support
At least, it works on IE9, FF3, Chrome 1, Safari 4, Opera 10.5.
There's also an IE version which works both on modern browsers and old versions of IE! (Works on IE8 and IE7, but can crash IE6).
Upvotes: 23
Reputation: 711
Like @benvie said, its more efficient to change a style sheet rather than using jQuery.css (which will loop through all of the elements in the set). It is also important not to add a new style to the head every time the function is called because it will create a memory leak and thousands of CSS rules that have to be individually applied by the browser. I would do something like this:
//Add the stylesheet once and store a cached jQuery object
var $style = $("<style type='text/css'>").appendTo('head');
function onResize() {
var css = "\
.someClass {\
left: "+leftVal+";\
width: "+widthVal+";\
height: "+heightVal+";\
}";
$style.html(css);
}
This solution will change your styles by modifying the DOM only once per resize. Note that for effective js minification and compression, you probably don't want to pretty-print the css, but I did for clarity.
Upvotes: 8
Reputation: 2014
Nice question. A lot of the answers here had a solution directly contradicting what you were asking
"I know how to use jQuery to assign width, height, etc. to an element, but what I'm trying to do is actually change the value defined in the stylesheet so that the dynamically-created value can be assigned to multiple elements.
"
jQuery .css
styles elements inline: it doesn't change the physical CSS rule! If you want to do this, I would suggest using a vanilla JavaScript solution:
document.styleSheets[0].cssRules[0].cssText = "\
#myID {
myRule: myValue;
myOtherRule: myOtherValue;
}";
This way, you're setting the stylesheet css rule, not appending an inline style.
Hope this helps!
Upvotes: 19
Reputation: 7045
YUI 2 and 3 has a module stylesheet that will let you do just that (edit stylesheets on the fly with javascript). http://yuilibrary.com/yui/docs/stylesheet/. So I think it is possible. This is not the same as $(".some").css({...}) but really change/add/remove styles definition from stylesheet, just like the user asked.
Upvotes: 1
Reputation: 71
Use jquery to add a style override in the <head>
:
$('<style>.someClass {color: red;} input::-webkit-outer-spin-button: {display: none;}</style>')
.appendTo('head');
Upvotes: 7
Reputation:
Contrary to some of the answers here, editing the stylesheet itself with Javascript is not only possible, but higher performance. Simply doing $('.myclass').css('color: red')
will end up looping through every item matching the selector and individually setting the style attribute. This is really inefficient and if you have hundreds of elements, it's going to cause problems.
Changing classes on the items is a better idea, but you still suffer from the same problem in that you're changing an attribute on N items, which could be a large number. A better solution might be to change the class on one single parent item or a small number of parents and then hit the target items using the "Cascade" in css. This serves in most situations, but not all.
Sometimes you need to change the CSS of a lot of items to something dynamic, or there's no good way for you to do so by hitting a small number of parents. Changing the stylesheet itself, or adding a small new one to override the existing css is an extremely efficient way to change the display of items. You're only interacting with the DOM in one spot and the browser can handle deploying those changes really efficiently.
jss is one library that helps make it easier to directly edit the stylesheet from javascript.
Upvotes: 30
Reputation: 5475
Why not just use a .class
selector to modify the properties of every object in that class?
ie:
$('.myclass').css('color: red;');
Upvotes: 2
Reputation: 41266
You can't modify the members of a CSS class on the fly. However, you could introduce a new <style>
tag on the page with your new css class implementation, and then switch out the class. Example:
Sample.css
.someClass { border: 1px solid black; font-size: 20px; }
You want to change that class entirely, so you create a new style element:
<style>
.someClassReplacement { border: 1px solid white; font-size: 14px; }
</style>
You then do a simple replacement via jQuery:
$('.someClass').removeClass('someClass').addClass('someClassReplacement');
Upvotes: 3