开发者

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 tho开发者_开发问答se 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.


Use jquery to add a style override in the <head>:

$('<style>.someClass {color: red;} input::-webkit-outer-spin-button: {display: none;}</style>')
.appendTo('head'); 


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

  • Clear DOM
  • You can modify the property you want without replacing the whole style

Disadvantages

  • Only internal CSS
  • You have to find specific style tag you want to edit

Hope it helps anyhow.


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');


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'});


Why not just use a .class selector to modify the properties of every object in that class?

ie:

$('.myclass').css('color: red;');


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.


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.


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>&nbsp;&nbsp;
<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.


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.


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.

  • Use a CSS Variable
  • Put the CSS variable where the static CSS class property value would normally be
  • Use code to set a new CSS variable value
  • The change to the CSS variable changes the CSS class property value and that causes the styling on the device screen to change

CSS - Styling

<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.


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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜