如何在JavaScript中动态创建CSS类并应用?

387

我需要在JavaScript中动态创建CSS样式表类,并将其分配给某些HTML元素,例如div、table、span、tr等,以及某些控件,如asp:Textbox、Dropdownlist和datalist。

这是否可能?

带有示例会更好。


2
请查看 https://github.com/Box9/jss - jedierikb
请参阅https://dev59.com/gnM_5IYBdhLWcg3w02z8。 - jantimon
请注意,在某些情况下,您真正想要的可能并不是创建一个CSS类并应用它,而是因为您想一次添加多个样式,可能因为这样更“快”(更容易输入),所以您来到了这里。在这种情况下,请查看此问题的答案,其中一些非常有用:https://dev59.com/_W865IYBdhLWcg3wIa_M - aderchox
17个回答

512

这里有一个选项:

var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.cssClass { color: #f00; }';
document.getElementsByTagName('head')[0].appendChild(style);

document.getElementById('someElementId').className = 'cssClass';
<div id="someElementId">test text</div>


11
我的使用场景是一个书签脚本,用于为质量保证目的突出显示特定元素。 - TomG
25
我很确定这会导致IE 8及以下版本出现未知的运行时错误。 - Andy Hume
33
另一个使用情景是,您希望有一个不依赖于 CSS 文件的单个 JS 库。在我的情况下,我想要开箱即用的轻量级 growl 样式的提示弹出窗口。 - xeolabs
2
我正在做类似w00t的事情。我正在开发一个交互式的HTML5应用程序,它将在画布上进行书写,并且我希望让用户从广泛的字体中选择使用。与其拥有一个很长的CSS来包含所有字体,我计划创建一个后端,在那里我只需上传字体数据,每当程序加载时,通过调用Web服务来获取字体并添加它们。 - CJLopez
5
我曾经需要一次性突出和取消突出页面上许多元素,与用户的选择相对应。为所有元素应用和删除类太慢了。因此,在浏览器中,当我的 ajax 请求返回数据后,我为每个可能的选择创建了一个 CSS 类,并将其应用于每个元素,定义所有没有属性的类规则。一旦做出选择,重写规则本身会以惊人的极快速度更新页面上的所有元素。所以,是的,有一个使用案例。 - ErikE
显示剩余13条评论

129
发现了一个更好的解决方案,它可以在所有浏览器中 运行。
使用 document.styleSheet 添加或替换规则。 接受的答案简短而方便,但这也适用于 IE8 及以下版本。
function createCSSSelector (selector, style) {
  if (!document.styleSheets) return;
  if (document.getElementsByTagName('head').length == 0) return;

  var styleSheet,mediaType;

  if (document.styleSheets.length > 0) {
    for (var i = 0, l = document.styleSheets.length; i < l; i++) {
      if (document.styleSheets[i].disabled) 
        continue;
      var media = document.styleSheets[i].media;
      mediaType = typeof media;

      if (mediaType === 'string') {
        if (media === '' || (media.indexOf('screen') !== -1)) {
          styleSheet = document.styleSheets[i];
        }
      }
      else if (mediaType=='object') {
        if (media.mediaText === '' || (media.mediaText.indexOf('screen') !== -1)) {
          styleSheet = document.styleSheets[i];
        }
      }

      if (typeof styleSheet !== 'undefined') 
        break;
    }
  }

  if (typeof styleSheet === 'undefined') {
    var styleSheetElement = document.createElement('style');
    styleSheetElement.type = 'text/css';
    document.getElementsByTagName('head')[0].appendChild(styleSheetElement);

    for (i = 0; i < document.styleSheets.length; i++) {
      if (document.styleSheets[i].disabled) {
        continue;
      }
      styleSheet = document.styleSheets[i];
    }

    mediaType = typeof styleSheet.media;
  }

  if (mediaType === 'string') {
    for (var i = 0, l = styleSheet.rules.length; i < l; i++) {
      if(styleSheet.rules[i].selectorText && styleSheet.rules[i].selectorText.toLowerCase()==selector.toLowerCase()) {
        styleSheet.rules[i].style.cssText = style;
        return;
      }
    }
    styleSheet.addRule(selector,style);
  }
  else if (mediaType === 'object') {
    var styleSheetLength = (styleSheet.cssRules) ? styleSheet.cssRules.length : 0;
    for (var i = 0; i < styleSheetLength; i++) {
      if (styleSheet.cssRules[i].selectorText && styleSheet.cssRules[i].selectorText.toLowerCase() == selector.toLowerCase()) {
        styleSheet.cssRules[i].style.cssText = style;
        return;
      }
    }
    styleSheet.insertRule(selector + '{' + style + '}', styleSheetLength);
  }
}

函数的使用方法如下。

createCSSSelector('.mycssclass', 'display:none');

2
已确认在IE8上工作正常。我不得不在mediaType的for循环ifs中添加“styleSheet.cssRules [i] .selectorText &&”和“styleSheet.rules [i] .selectorText &&”,因为它在Chrome中无法工作,显然有时选择器文本未定义。 - w00t
2
@dnuske 我也遇到了同样的问题。原来是styleSheet.cssRules评估为null。我使用的解决方法是创建一个新变量var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0,并在函数实现中替换它的使用。 - Raj Nathani
@Vishwanath 我在使用这个函数时,在Firefox中遇到了一个安全错误。你能确认一下吗? - Matt Baker
@Vishwanath 是的,这个错误在Firefox中触发:https://dev59.com/3WEi5IYBdhLWcg3wG45o - Matt Baker
这个解决方案在跨平台上表现良好,并且在修改现有选择器时不会添加不必要的规则。 - Stephanie
显示剩余7条评论

33

简短回答,这是“所有浏览器”兼容的(具体来说,包括IE8/7):

function createClass(name,rules){
    var style = document.createElement('style');
    style.type = 'text/css';
    document.getElementsByTagName('head')[0].appendChild(style);
    if(!(style.sheet||{}).insertRule) 
        (style.styleSheet || style.sheet).addRule(name, rules);
    else
        style.sheet.insertRule(name+"{"+rules+"}",0);
}
createClass('.whatever',"background-color: green;");

最后一步是将该类应用于元素:

function applyClass(name,element,doRemove){
    if(typeof element.valueOf() == "string"){
        element = document.getElementById(element);
    }
    if(!element) return;
    if(doRemove){
        element.className = element.className.replace(new RegExp("\\b" + name + "\\b","g"));
    }else{      
        element.className = element.className + " " + name;
    }
}

这里还有一个小测试页面:https://gist.github.com/shadybones/9816763

关键的一点是style元素有一个"styleSheet"/"sheet"属性,您可以使用它来添加/删除规则。


1
那么这会在每个类创建时都创建一个新的“style”元素吗?所以,如果我基于数据在for循环中创建1000多个类,这将需要应用document.head.appendChild 1000次? - B''H Bi'ezras -- Boruch Hashem
1
在Chrome中,style.sheet和style.styleSheet不存在。 - B''H Bi'ezras -- Boruch Hashem
1
至少在2022年1月份,它在Chrome中不再起作用。 - UnDiUdin
如果您创建一个通过ID引用的类,例如“#myItem {color:red}”,则甚至不需要调用“applyClass”,浏览器会自动将其添加到CSS样式表规则中。 - andreszs

18

有一个轻量级的jQuery插件可以生成CSS声明:jQuery-injectCSS

事实上,它使用JSS(用JSON描述的CSS),但很容易处理以生成动态CSS样式表。

$.injectCSS({
    "#test": {
        height: 123
    }
});

请创建一个使用jQuery在运行时生成CSS规则类的代码,类似于https://dev59.com/gnM_5IYBdhLWcg3w02z8#1214217。 - user1742529

8

这个函数可以简单地将一个或多个新的级联样式表规则附加到文档。

例如,此示例会将cursor:pointer附加到每个buttoninputselect元素中。

document.body.appendChild(Object.assign(document.createElement("style"), {textContent: "select, button, input {cursor:pointer}"}))

7

我认为YUI是目前最好的样式表工具,我鼓励你去了解一下,以下是其中一些特点:

// style element or locally sourced link element
var sheet = YAHOO.util.StyleSheet(YAHOO.util.Selector.query('style',null,true));

sheet = YAHOO.util.StyleSheet(YAHOO.util.Dom.get('local'));


// OR the id of a style element or locally sourced link element
sheet = YAHOO.util.StyleSheet('local');


// OR string of css text
var css = ".moduleX .alert { background: #fcc; font-weight: bold; } " +
          ".moduleX .warn  { background: #eec; } " +
          ".hide_messages .moduleX .alert, " +
          ".hide_messages .moduleX .warn { display: none; }";

sheet = new YAHOO.util.StyleSheet(css);

显然还有其他更简单的方法可以在运行时更改样式,例如此处提供的建议。如果它们对您的问题有意义,那么它们可能是最好的选择,但肯定存在修改CSS是更好的解决方案的原因。最明显的情况是当您需要修改大量元素时。另一个重要情况是如果您需要让样式更改涉及级联。使用DOM来修改元素将始终具有更高的优先级。这是一个巨力的方法,相当于直接在HTML元素上使用 style 属性。这并不总是期望的效果。


6

这里是Vishwanath的解决方案,稍微重新编写了一下并添加了注释:

function setStyle(cssRules, aSelector, aStyle){
    for(var i = 0; i < cssRules.length; i++) {
        if(cssRules[i].selectorText && cssRules[i].selectorText.toLowerCase() == aSelector.toLowerCase()) {
            cssRules[i].style.cssText = aStyle;
            return true;
        }
    }
    return false;
}

function createCSSSelector(selector, style) {
    var doc = document;
    var allSS = doc.styleSheets;
    if(!allSS) return;

    var headElts = doc.getElementsByTagName("head");
    if(!headElts.length) return;

    var styleSheet, media, iSS = allSS.length; // scope is global in a function
    /* 1. search for media == "screen" */
    while(iSS){ --iSS;
        if(allSS[iSS].disabled) continue; /* dont take into account the disabled stylesheets */
        media = allSS[iSS].media;
        if(typeof media == "object")
            media = media.mediaText;
        if(media == "" || media=='all' || media.indexOf("screen") != -1){
            styleSheet = allSS[iSS];
            iSS = -1;   // indication that media=="screen" was found (if not, then iSS==0)
            break;
        }
    }

    /* 2. if not found, create one */
    if(iSS != -1) {
        var styleSheetElement = doc.createElement("style");
        styleSheetElement.type = "text/css";
        headElts[0].appendChild(styleSheetElement);
        styleSheet = doc.styleSheets[allSS.length]; /* take the new stylesheet to add the selector and the style */
    }

    /* 3. add the selector and style */
    switch (typeof styleSheet.media) {
    case "string":
        if(!setStyle(styleSheet.rules, selector, style));
            styleSheet.addRule(selector, style);
        break;
    case "object":
        if(!setStyle(styleSheet.cssRules, selector, style));
            styleSheet.insertRule(selector + "{" + style + "}", styleSheet.cssRules.length);
        break;
    }

5

从IE 9开始,您现在可以加载文本文件并设置style.innerHTML属性。因此,您现在可以通过ajax加载css文件(并获取回调),然后只需像这样设置style标签内的文本。

这在其他浏览器中也可以工作,但不确定有多久历史。但只要您不需要支持IE8,则可以正常工作。

// RESULT: doesn't work in IE8 and below. Works in IE9 and other browsers.
$(document).ready(function() {
    // we want to load the css as a text file and append it with a style.
    $.ajax({
        url:'myCss.css',
        success: function(result) {
            var s = document.createElement('style');
            s.setAttribute('type', 'text/css');
            s.innerHTML = result;
            document.getElementsByTagName("head")[0].appendChild(s);
        },
        fail: function() {
            alert('fail');
        }
    })
});

然后,您可以让它拉取一个外部文件,例如 myCss.css

.myClass { background:#F00; }

5

https://jsfiddle.net/xk6Ut/256/

在JavaScript中动态创建和更新CSS类的一种方法:

  • 使用Style元素创建CSS部分
  • 为样式元素使用ID,以便我们可以更新CSS类

.....

function writeStyles(styleName, cssText) {
    var styleElement = document.getElementById(styleName);
    if (styleElement) 
             document.getElementsByTagName('head')[0].removeChild(
        styleElement);
    styleElement = document.createElement('style');
    styleElement.type = 'text/css';
    styleElement.id = styleName;
    styleElement.innerHTML = cssText;
    document.getElementsByTagName('head')[0].appendChild(styleElement);
}

...

    var cssText = '.testDIV{ height:' + height + 'px !important; }';
    writeStyles('styles_js', cssText)

4

使用Google Closure:

您可以直接使用ccsom模块:

goog.require('goog.cssom');
var css_node = goog.cssom.addCssText('.cssClass { color: #F00; }');

该Javascript代码在将CSS节点放入文档头部时尝试跨浏览器兼容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接