¿Cómo crear una etiqueta <style> con Javascript?

336

Estoy buscando una manera de insertar una <style>etiqueta en una página HTML con JavaScript.

La mejor forma que encontré hasta ahora:

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

Esto funciona en Firefox, Opera e Internet Explorer pero no en Google Chrome. También es un poco feo con el <br>frente para IE.

¿Alguien sabe de una manera de crear una <style>etiqueta que

  1. Es mejor

  2. Funciona con Chrome?

O tal vez

  1. Esto es algo no estándar que debo evitar

  2. Tres navegadores son geniales y ¿quién usa Chrome de todos modos?

Ameena sana
fuente

Respuestas:

663

Intente agregar el styleelemento a la en headlugar de a la body.

Esto se probó en IE (7-9), Firefox, Opera y 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));
}
Christoph
fuente
19
FYI, document.heades compatible con todos los principales navegadores.
Rob W
30
@RobW: no es compatible con IE8 y versiones inferiores. Es compatible con los navegadores modernos, pero no con todos los principales.
Tim
55
¿Por qué no solo usar document.querySelector("head")? Es el evento apoyado por IE8 por fuente
allenhwkim
8
@allenhwkim: la respuesta es anterior a la introducción de querySelector(); hoy, probablemente iría document.head, disponible desde IE9
Christoph
2
Tuve que agregar el elemento de estilo a la cabeza antes de acceder style.styleSheet.cssText. Antes de que se añadiera style.styleSheetera nulo.
Will Hawker
38

Aquí hay un script que agrega estilo IE createStyleSheet()y addRule()métodos a los navegadores que no los tienen:

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

Puede agregar archivos externos a través de

document.createStyleSheet('foo.css');

y crear dinámicamente reglas a través de

var sheet = document.createStyleSheet();
sheet.addRule('h1', 'background: red;');
Christoph
fuente
Esto fue muy útil para mí al intentar cargar un archivo CSS externo en un contexto extraño de CMS. Tuve algunos problemas con la parte addRule / removeRule, así que los eliminé y todo funciona bien.
Kirkman14
33

Supongo que desea insertar una styleetiqueta en lugar de una linketiqueta (haciendo referencia a un CSS externo), así que eso es lo que hace el siguiente ejemplo:

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

Además, noté en su pregunta que está utilizando innerHTML . Esta es en realidad una forma no estándar de insertar datos en una página. La mejor práctica es crear un nodo de texto y agregarlo a otro nodo de elemento.

Con respecto a su pregunta final, escuchará a algunas personas decir que su trabajo debería funcionar en todos los navegadores. Todo depende de tu audiencia. Si nadie en su audiencia está usando Chrome, entonces no se preocupe; sin embargo, si está buscando llegar a la mayor audiencia posible, entonces es mejor admitir todos los principales navegadores de grado A

Tom
fuente
1
Esto es básicamente lo mismo que mi solución; ninguno de ellos funciona en IE!
Christoph
Agregar un nodo de texto no funciona en IE. ¡Pero poner el elemento de estilo en la cabeza hace que mi solución funcione en Chrome! :-)
1
para IE, usestyleNode.styleSheet.cssText = ...
Christoph
77
(Comentario muy tardío) "Todo depende de tu audiencia. Si nadie en tu audiencia está usando Chrome, entonces no te preocupes" Esta actitud es lo que lleva a las personas a estar encerradas en IE6 durante años después de que IE7 e incluso IE8 estén disponibles . "Pero la aplicación web que tengo que usar solo funciona en IE6"
Stephen P
1
Sí, tienes razón y tampoco estoy abogando necesariamente por hacerlo (de ahí el todo: "si estás buscando llegar a la mayor audiencia posible, entonces es mejor apoyar a todos los principales navegadores de grado A"), pero conociendo el La audiencia para su plataforma es importante.
Tom
31

<style>las etiquetas deben estar ubicadas dentro del <head>elemento, y cada etiqueta agregada debe agregarse al final de la <head>etiqueta.

Usando insertAdarestHTML para inyectar una etiqueta de estilo en la etiqueta del encabezado del documento :

DOM nativo:

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>

vsync
fuente
De acuerdo, es mejor y simple, pero ten cuidado con CSS mal inyectado si no tienes el control sobre él.
Sin fin
24

Un ejemplo que funciona y cumple con todos los navegadores:

var ss = document.createElement("link");
ss.type = "text/css";
ss.rel = "stylesheet";
ss.href = "style.css";
document.getElementsByTagName("head")[0].appendChild(ss);
belaz
fuente
77
elemento incorrecto: styleno tiene relo hrefatributo, ¿quiso decir link?
Christoph
44
¿Qué linktiene que ver el elemento con la pregunta? el OP solicitó styleelemento. Esta respuesta no tiene nada que ver con la pregunta
vsync
2
@vsync stylees para agregar estilos en línea, linkes correcto para las fuentes de hojas de estilo y tiene la ventaja de evitar problemas entre navegadores.
majick
7

A menudo es necesario anular las reglas existentes, por lo que agregar nuevos estilos al HEAD no funciona en todos los casos.

Se me ocurrió esta función simple que resume todos los enfoques no válidos de "agregar al cuerpo" y es más conveniente de usar y depurar (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);

Demostración: codepen , jsfiddle

Garlaro
fuente
Funcionó perfectamente en los últimos Firefox, Chrome y en ie8 (o al menos la forma en que lo escribí). Realmente no sé por qué esto no tiene más puntos.
Harry Pehkonen
Esto no tiene más puntos, porque no es válido, ni tampoco es bueno inyectar <style>etiqueta fuera de la <head>etiqueta. La etiqueta de estilo no debería aparecer en ningún otro lugar sino dentro de la <head>etiqueta. Ese es un estándar html ampliamente adoptado. Hay un atributo de estilo para el estilo en línea y el atributo de estilo se puede aplicar a cualquier etiqueta dentro de la <body>etiqueta, incluida la <body>etiqueta propia.
Spooky
Este código no me funcionó. Creé un código mucho más corto que funciona en tres navegadores y publicaré mi respuesta aquí.
David Spector
No pude entender por qué Chrome no estaba actualizando mis estilos cuando agregué un bloque de estilo a la cabeza dinámicamente. Intenté esto y mágicamente se actualiza. Necesito investigar cuál es realmente la diferencia aquí en este código que activa el navegador para volver a renderizar el CSS. Pero, muchas gracias!
prograhammer el
5

Esta variable de objeto agregará una etiqueta de estilo a la etiqueta principal con un atributo de tipo y una regla de transición simple dentro que coincida con cada id / clase / elemento. Siéntase libre de modificar la propiedad del contenido e inyectar tantas reglas como necesite. Solo asegúrese de que las reglas css dentro del contenido permanezcan en una línea (o 'escape' cada nueva línea, si así lo prefiere).

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();
Escalofriante
fuente
1
Me gustó esta solución, así que la utilicé para cambiar el tamaño de los botones en dispositivos móviles, pero es incorrecto que la propiedad de contenido deba estar en una línea: simplemente concatene varias cadenas (sobre varias líneas) con el operador +.
RufusVS
Cierto. Lo sé y lo sabía. Pero, a veces, soy demasiado vago para escribir como se supone que debo hacerlo. : D Saludos.
Espeluznante
5

Aquí hay una variante para agregar dinámicamente una clase

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);
    }
  }
}
Miguel
fuente
4

Esta función inyectará CSS cada vez que llame a la función de appendStyleesta manera:
appendStyle('css you want to inject')

Esto funciona inyectando un stylenodo en el encabezado del documento. Esta es una técnica similar a la que se usa comúnmente para cargar JavaScript de forma diferida. Funciona de manera consistente en la mayoría de los navegadores modernos.

appendStyle = function (content) {
  style = document.createElement('STYLE');
  style.type = 'text/css';
  style.appendChild(document.createTextNode(content));
  document.head.appendChild(style);
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <h1>Lorem Ipsum</h1>
  <p>dolar sit amet</p>
  <button onclick='appendStyle("body { background-color: #ff0000;}h1 { font-family: Helvetica, sans-serif; font-variant: small-caps; letter-spacing: 3px; color: #ff0000; background-color: #000000;}p { font-family: Georgia, serif; font-size: 14px; font-style: normal; font-weight: normal; color: #000000; background-color: #ffff00;}")'>Press me to inject CSS!</button>
</body>

</html>

También puede cargar archivos CSS externos de forma diferida utilizando el siguiente fragmento de código:

appendExternalStyle = function (content) {
  link = document.createElement('LINK');
  link.rel = 'stylesheet';
  link.href = content;
  link.type = 'text/css';
  document.head.appendChild(link);
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    html {
      font-family: sans-serif;
      font-display: swap;
    }
  </style>
</head>

<body>

  <h1>Lorem Ipsum</h1>
  <p>dolar sit amet</p>
  <button onclick='appendExternalStyle("data:text/css;base64,OjotbW96LXNlbGVjdGlvbntjb2xvcjojZmZmIWltcG9ydGFudDtiYWNrZ3JvdW5kOiMwMDB9OjpzZWxlY3Rpb257Y29sb3I6I2ZmZiFpbXBvcnRhbnQ7YmFja2dyb3VuZDojMDAwfWgxe2ZvbnQtc2l6ZToyZW19Ym9keSxodG1se2NvbG9yOnJnYmEoMCwwLDAsLjc1KTtmb250LXNpemU6MTZweDtmb250LWZhbWlseTpMYXRvLEhlbHZldGljYSBOZXVlLEhlbHZldGljYSxzYW5zLXNlcmlmO2xpbmUtaGVpZ2h0OjEuNjd9YnV0dG9uLGlucHV0e292ZXJmbG93OnZpc2libGV9YnV0dG9uLHNlbGVjdHstd2Via2l0LXRyYW5zaXRpb24tZHVyYXRpb246LjFzO3RyYW5zaXRpb24tZHVyYXRpb246LjFzfQ==")'>press me to inject css!</button>
</body>

</html>

Joel Ellis
fuente
Este es aproximadamente el enfoque que desarrollé independientemente. Corto y dulce, y funciona en los últimos navegadores Firefox, Microsoft Edge y Chrome. Por supuesto, hay demasiados errores de JavaScript en Internet Explorer para preocuparse de incluirlo en la compatibilidad del navegador.
David Spector
3

Tu escribiste:

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

¿Por qué no esto?

var styleNode = document.createElement("style");
document.head.appendChild(styleNode);

En adelante, puede agregar reglas CSS fácilmente al código HTML:

styleNode.innerHTML = "h1 { background: red; }\n";
styleNode.innerHTML += "h2 { background: green; }\n";

... o directamente al DOM:

styleNode.sheet.insertRule("h1 { background: red; }");
styleNode.sheet.insertRule("h2 { background: green; }");

Espero que esto funcione en todas partes, excepto en los navegadores arcaicos.

Definitivamente funciona en Chrome en el año 2019.

7vujy0f0hy
fuente
3

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

Con mucho, la solución más sencilla. Todo lo que tiene que hacer es escribir lo mismo que normalmente declararía las styleetiquetas, entre las teclas de retroceso

Kabirr singh sahni
fuente
1
Hola y bienvenidos al sitio. Si bien este código puede resolver el problema dado, también debe incluir una explicación de cómo y por qué funciona.
Radiodef
1

Si el problema que enfrenta es inyectar una cadena de CSS en una página, es más fácil hacerlo con el <link> elemento que<style> elemento.

Lo siguiente agrega una p { color: green; }regla a la página.

<link rel="stylesheet" type="text/css" href="data:text/css;charset=UTF-8,p%20%7B%20color%3A%20green%3B%20%7D" />

Puede crear esto en JavaScript simplemente codificando su cadena de CSS y agregando el HREFatributo. Mucho más simple que todas las peculiaridades de los <style>elementos o acceder directamente a las hojas de estilo.

let linkElement: HTMLLinkElement = this.document.createElement('link');
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('type', 'text/css');
linkElement.setAttribute('href', 'data:text/css;charset=UTF-8,' + encodeURIComponent(myStringOfstyles));

Esto funcionará en IE 5.5 hacia arriba

Moefinley
fuente
Inyectar el bloque de estilo dinámicamente en la cabeza no actualizó el estilo en algunos casos. ¡Esto funciona sin embargo!
prograhammer el
-1
this link may helpful to you: 

http://jonraasch.com/blog/javascript-style-node

Vinod Poorma
fuente
No utilice bloques de texto / código preformateados para un párrafo corto. Vincular una respuesta de esta manera también es inútil. Resume primero el sitio web.
FryingggPannn