Modifying CSS class property values on the fly with JavaScript / jQuery
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.
My Question:
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.
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.
Demo, IE demo
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:
- The function is written in vanilla-js, so it has better performance than jQuery alternatives
- One stylesheet is created after the first call to
setStyle
, so if you don't call it, it won't create any stylesheet. - The same stylesheet is reused for the following calls of
setStyle
- The function return a reference to the node associated with the bunch of CSS that you have added. If you call the function again with that node as a second argument, it will replace the old CSS with the new one.
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).
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!
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.