如何使用Javascript创建<style>标签?

447

我正在寻找一种使用JavaScript将<style>标签插入HTML页面的方法。

目前我发现的最好的方式:

var divNode = document.createElement("div");
divNode.innerHTML = "<br><style>h1 { background: red; }</style>";
document.body.appendChild(divNode);

这在Firefox、Opera和Internet Explorer中工作,但在Google Chrome中不起作用。而且在IE中,前面的<br>有点丑。

是否有人知道创建一个<style>标签的方法:

  1. 更好看

  2. 与Chrome兼容?

或者可能:

  1. 这是我应该避免的非标准做法

  2. 三个可用的浏览器很棒,谁还使用Chrome呢?

19个回答

807
尝试将style元素添加到head而不是body。这在IE(7-9),Firefox,Opera和Chrome中进行了测试。
var css = 'h1 { background: red; }',
    head = document.head || document.getElementsByTagName('head')[0],
    style = document.createElement('style');

head.appendChild(style);

style.type = 'text/css';
if (style.styleSheet){
  // This is required for IE8 and below.
  style.styleSheet.cssText = css;
} else {
  style.appendChild(document.createTextNode(css));
}

26
FYI,document.head在所有主流浏览器中都得到支持。 - Rob W
32
不支持IE8及以下版本,现代浏览器支持但不是所有主流浏览器都支持。 - Tim
5
为什么不直接使用 document.querySelector("head")?根据来源,它也支持IE8。 - allenhwkim
8
回答早于querySelector()的引入;今天,我可能会选择自IE9起可用的document.head - Christoph
2
在访问style.styleSheet.cssText之前,我必须将样式元素附加到头部。在附加之前,style.styleSheet为null。 - Will Hawker
1
请注意:HTMLStyleElement.type是只读属性,因此不应该被设置。它也已经被弃用,不再使用。 更多信息请参见MDN:https://developer.mozilla.org/en-US/docs/Web/API/HTMLStyleElement/type - Julian

110
请将 <style> 标签放在 <head> 元素内,每个添加的标签应该被添加到 <head> 标签底部。
使用 insertAdjacentHTML 将样式标签注入文档 head 标签

原生 DOM:

document.head.insertAdjacentHTML("beforeend", `<style>body{background:red}</style>`)


jQuery:

$('<style>').text("body{background:red}").appendTo(document.head)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


2
同意,这是最好和简单的方法,但要注意如果您无法控制它,可能会出现注入错误的CSS! - Endless

50
const style = document.createElement("style")
style.textContent = "h1 { background-color: red; }"
document.head.appendChild(style)

现代化且简单的方式

上面的代码就是要点;如果您想了解原因,请继续阅读。

为什么需要另一个答案?已接受的答案过时了,其中包括针对像Internet Explorer这样的过时浏览器的冗余代码。其他答案过于复杂或使用了像.innerHTML这样的属性,这可能导致跨站脚本攻击。

type属性不是必需的

大多数答案都会设置type属性,例如:style.type = "text/css"。除非您需要支持旧版浏览器,否则无需设置此属性。

根据<style>: The Style Information element - HTML | MDNtype属性是可选的,并默认为text/css

type

此属性将样式语言定义为MIME类型(不应指定字符集)。此属性是可选的,如果未指定,则默认为text/css;除空字符串或text/css之外的值均不使用。注意:在现代Web文档中很少有理由包含此属性。

添加CSS

要添加CSS,请使用.textContent,因为它比其他方法更安全、更快。与.innerHTML相反,它不解析HTML,因此可以防止跨站脚本攻击

另一个类似的属性.innerText类似于.textContent,但会考虑CSS样式,并仅表示“已呈现”的文本内容。由于我们不关心仅“已呈现”的内容,因此我们更喜欢.textContent

设置.textContent有什么作用?

设置.textContent属性会删除所有节点(元素)的子节点,并用给定的字符串值替换它们。

在哪里放置元素?

样式元素应该包含在头部: "样式元素 <style> 必须包含在文档的 <head> 中。...". [<style> ... | MDN]

要获取头部,请使用document.head,因为它已经被所有主流浏览器支持了很长时间,所以不需要其他备选方案。


通过这种方式注入样式似乎无法改变<body>的样式? - TheRoadrunner
我不明白为什么这是不可能的;也许我误解了你的问题?它的工作方式与直接在HTML中编写样式标签相同。也许你有语法错误或者在你添加的样式之后定义了另一个样式? - touchmarine
我已注入了以下样式代码,并且对于 body 内的所有元素都可以正常工作,但是我无法将 body 边距设为 0,它仍然保持默认的 8px。如果我在预定义的样式部分中执行同样的操作,我可以将 body 页边距设为 0。 - TheRoadrunner
基于它对其他元素有效,应该也可以对body元素有效。似乎有一些东西干扰了body的样式。也许可以尝试逐个删除其他样式和脚本,直到生效为止? - touchmarine
2
这是最好的答案。大约在2010年左右的旧答案并没有错,只是已经过时了。感谢提供最新的答案,并且我很欣赏关于为什么一些旧的繁琐步骤不再需要的解释。 - Martin

39

我假设你想要插入一个 style 标签而不是一个链接到外部 CSS 的 link 标签,下面的示例就是这样做的:

<html>
 <head>
  <title>Example Page</title>
 </head>
 <body>
  <span>
   This is styled dynamically via JavaScript.
  </span>
 </body>
 <script type="text/javascript">
   var styleNode = document.createElement('style');
   styleNode.type = "text/css";
   // browser detection (based on prototype.js)
   if(!!(window.attachEvent && !window.opera)) {
        styleNode.styleSheet.cssText = 'span { color: rgb(255, 0, 0); }';
   } else {
        var styleText = document.createTextNode('span { color: rgb(255, 0, 0); } ');
        styleNode.appendChild(styleText);
   }
   document.getElementsByTagName('head')[0].appendChild(styleNode);
 </script>
</html>

此外,我注意到在你的问题中你正在使用innerHTML。这实际上是一种非标准的向页面插入数据的方式。最佳实践是创建一个文本节点并将其附加到另一个元素节点。

关于你最后的问题,你会听到有些人说你的工作应该在所有浏览器上都能运行。这取决于你的受众。如果你的听众中没有人使用Chrome,那就不用担心;然而,如果你想要达到尽可能大的受众,最好支持所有主要的A级浏览器。


2
这基本上与我的解决方案相同;它们都不能在IE中工作! - Christoph
2
对于IE浏览器,请使用 styleNode.styleSheet.cssText = ... - Christoph
1
好的,很棒。我更新了上面的代码,加入了浏览器检测,这样它就能正确地应用动态样式添加了。 - Tom
9
这完全取决于您的受众。如果您的受众中没有人使用Chrome,那就不必担心。这种态度导致人们在IE7和IE8发布后仍然被锁定在IE6上多年。 "但是我必须使用的Web应用程序只能在IE6上运行"。 - Stephen P
2
是的,你说得对,我也不一定主张这样做(因此整个句子是:“如果你想要覆盖尽可能多的受众,最好支持所有主流A级浏览器”),但了解你平台的受众很重要。 - Tom
显示剩余4条评论

39

这是一段脚本,可以在没有IE-style createStyleSheet()addRule()方法的浏览器中添加它们:

if(typeof document.createStyleSheet === 'undefined') {
    document.createStyleSheet = (function() {
        function createStyleSheet(href) {
            if(typeof href !== 'undefined') {
                var element = document.createElement('link');
                element.type = 'text/css';
                element.rel = 'stylesheet';
                element.href = href;
            }
            else {
                var element = document.createElement('style');
                element.type = 'text/css';
            }

            document.getElementsByTagName('head')[0].appendChild(element);
            var sheet = document.styleSheets[document.styleSheets.length - 1];

            if(typeof sheet.addRule === 'undefined')
                sheet.addRule = addRule;

            if(typeof sheet.removeRule === 'undefined')
                sheet.removeRule = sheet.deleteRule;

            return sheet;
        }

        function addRule(selectorText, cssText, index) {
            if(typeof index === 'undefined')
                index = this.cssRules.length;

            this.insertRule(selectorText + ' {' + cssText + '}', index);
        }

        return createStyleSheet;
    })();
}

你可以通过以下方式添加外部文件

document.createStyleSheet('foo.css');

并通过动态方式创建规则

var sheet = document.createStyleSheet();
sheet.addRule('h1', 'background: red;');

这对我非常有帮助,因为我试图在奇怪的CMS上下文中加载外部CSS文件。我确实遇到了一些addRule / removeRule方面的问题,所以我只是将它们删除了,现在一切都正常工作。 - Kirkman14

29

一个适用于所有浏览器且可行的示例:

var ss = document.createElement("link");
ss.type = "text/css";
ss.rel = "stylesheet";
ss.href = "style.css";
document.getElementsByTagName("head")[0].appendChild(ss);

7
错误元素:style 没有 relhref 属性 - 你是不是想用 link 元素? - Christoph
4
“link”元素与问题有什么关系?原帖询问的是“style”元素。这个回答与问题无关。 - vsync
3
@vsync 中的 style 用于添加内联样式,而正确的样式表来源应该使用 link,这有避免浏览器兼容性问题的优势。 - majick

10

document.head.innerHTML += `
  <style>
    h1 {
      color: red; 
    }
    p {
      color: blue;
    }
  </style>`
<h1>I'm red!</h1>
<p>I'm blue!</p>

到目前为止,最简单的解决方案是使用反引号括起来,就像平常声明style标签一样。


1
你好,欢迎来到本站。虽然这段代码可以解决问题,但你也应该包含一个解释它如何以及为什么能够工作的说明。 - Radiodef
⚠ 警告:我刚刚修复了一个使用这种代码的错误。这是一个糟糕的解决方案,因为它会导致整个头部内容重新计算,并重新应用所有样式和其他元素,从而引起闪烁和破坏。 - Paul

8

通常需要覆盖现有规则,因此将新样式附加到HEAD中并不总是有效。

我想到了这个简单的函数,它总结了所有无效的“附加到BODY”的方法,并且更方便使用和调试(IE8+)。

window.injectCSS = (function(doc){
    // wrapper for all injected styles and temp el to create them
    var wrap = doc.createElement('div');
    var temp = doc.createElement('div');
    // rules like "a {color: red}" etc.
    return function (cssRules) {
        // append wrapper to the body on the first call
        if (!wrap.id) {
            wrap.id = 'injected-css';
            wrap.style.display = 'none';
            doc.body.appendChild(wrap);
        }
        // <br> for IE: http://goo.gl/vLY4x7
        temp.innerHTML = '<br><style>'+ cssRules +'</style>';
        wrap.appendChild( temp.children[1] );
    };
})(document);

Demo: codepen, jsfiddle


在最新的Firefox、Chrome和ie8中都完美运行(或者至少是我输入的方式)。我真的不知道为什么这个没有更多的点赞。 - Harry Pehkonen
这段代码没有更多的要点,因为它不是有效的代码,也不是好的做法将<style>标签注入到<head>标签之外。<style>标签应该只出现在<head>标签内部,这是广泛采用的HTML标准。对于内联样式,可以使用style属性,并且style属性可以应用于<body>标签内的任何标签,包括<body>标签本身。 - Spooky
这段代码对我来说没有用。我编写了一些更短的代码,在三个浏览器中都可以运行,并将我的答案发布在这里。 - David Spector
我一直无法弄清楚为什么当我动态地向头部添加样式块时,Chrome没有更新我的样式。我尝试了这个方法,神奇地它更新了。我需要调查一下这段代码中实际上是什么不同的东西触发了浏览器重新渲染CSS。但是,非常感谢! - prograhammer

6
这里是动态添加类的一种变体。
function setClassStyle(class_name, css) {
  var style_sheet = document.createElement('style');
  if (style_sheet) {
    style_sheet.setAttribute('type', 'text/css');
    var cstr = '.' + class_name + ' {' + css + '}';
    var rules = document.createTextNode(cstr);
    if(style_sheet.styleSheet){// IE
      style_sheet.styleSheet.cssText = rules.nodeValue;
    } else {
      style_sheet.appendChild(rules);
    }
    var head = document.getElementsByTagName('head')[0];
    if (head) {
      head.appendChild(style_sheet);
    }
  }
}

5

这个对象变量将在头标签中添加一个带有类型属性和一个简单的过渡规则的样式标签,该规则匹配每个单独的id/class/元素。随意修改content属性并注入尽可能多的规则。只需确保content内部的CSS规则保持在一行(或者如果您喜欢,可以“转义”每个新行)。

var script = {

  type: 'text/css', style: document.createElement('style'), 
  content: "* { transition: all 220ms cubic-bezier(0.390, 0.575, 0.565, 1.000); }",
  append: function() {

    this.style.type = this.type;
    this.style.appendChild(document.createTextNode(this.content));
    document.head.appendChild(this.style);

}}; script.append();

1
我喜欢这个解决方案,所以我自己使用它来更改移动设备上的按钮大小,但是将内容属性放在一行上是不正确的:只需使用+运算符连接多个字符串(跨多行)。 - RufusVS
没错,我知道并且一直都知道。但是有时候,我就是太懒了,不想按照规定的方式写代码。哈哈,干杯! - Spooky

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