¿Cómo crear dinámicamente una clase CSS en JavaScript y aplicarla?

Respuestas:

394

Aunque no estoy seguro de por qué desea crear clases CSS con JavaScript, aquí hay una opción:

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';
I.devries
fuente
10
Mi caso de uso es un bookmarklet que destaca ciertos elementos para fines de control de calidad.
TomG
25
Estoy bastante seguro de que esto produce un error de tiempo de ejecución desconocido en IE 8 y menos.
Andy Hume
1
Mi caso de uso es cargar una fuente web aleatoria de Google y luego dar a la clase randomFont la familia de fuentes :-)
w00t
26
Otro caso de uso sería donde desea una única biblioteca JS sin dependencias de los archivos CSS. En mi caso, quiero ventanas emergentes de alerta de estilo gruñido livianas listas para usar.
xeolabs
1
Estoy haciendo algo similar como w00t. Estoy trabajando en una aplicación html5 interactiva, que tendrá escritura en un lienzo, y quiero permitir que mi usuario seleccione entre una amplia gama de fuentes para usar. En lugar de tener un CSS demasiado largo con toda la fuente, planeo crear un backend donde simplemente cargue los datos de la fuente y cada vez que se cargue el programa, una pequeña llamada a un servicio web trae la fuente y los agrega
CJLopez
117

Encontró una mejor solución, que funciona en todos los navegadores.
Utiliza document.styleSheet para agregar o reemplazar reglas. La respuesta aceptada es breve y útil, pero esto funciona en IE8 y menos también.

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

La función se usa de la siguiente manera.

createCSSSelector('.mycssclass', 'display:none');
Vishwanath
fuente
2
Confirmado trabajando con IE8. Tuve que agregar un "styleSheet.cssRules [i] .selectorText &&" y "styleSheet.rules [i] .selectorText &&" en el mediaType for-loop ifs porque no funcionaba en Chrome, aparentemente a veces el selectorText no está t definido.
w00t
@ w00t ¿Podría pegar o editar el código para que funcione?
Hengjie
Acabo de abrir Chrome (Versión 34.0.1847.132) pegué las funciones y las ejecuté, pero no funcionó: "TypeError: No se puede leer la propiedad 'length' de null". ¿Puede ser que no funciona creándolo desde la consola del desarrollador?
dnuske
Resulta que algunas versiones de cromo (o cromo) no permiten insertRule en el índice 0. Aquí está la solución: styleSheet.insertRule (selector + "{" + style + "}", styleSheet.cssRules.length);
Dnuske 05 de
1
@dnuske Encontré el mismo problema. Resulta que styleSheet.cssRules se evalúa como nulo. La solución que he usado es crear una nueva variable var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0y sustituir su uso sobre la implementación de la función.
Raj Nathani
27

Respuesta corta, esto es compatible "en todos los navegadores" (específicamente, 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;");

Y este bit final aplica la clase a un elemento:

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

Aquí también hay una pequeña página de prueba: https://gist.github.com/shadybones/9816763

La clave es el hecho de que los elementos de estilo tienen una propiedad "styleSheet" / "sheet" que puede usar para agregar / eliminar reglas.

shadybones
fuente
¿Entonces esto crea un nuevo elemento de "estilo" en cada creación de clase? Entonces, si tuviera que crear más de 1000 clases en un ciclo for basado en datos, ¿esto necesitaría aplicar document.head.appendChild 1000 veces?
bluejayke
para mí en Chrome style.sheet y style.styleSheet no existe
bluejayke
17

Hay un complemento jQuery ligero que permite generar declaraciones CSS: jQuery-injectCSS

De hecho, utiliza JSS (CSS descrito por JSON), pero es bastante fácil de manejar para generar hojas de estilo CSS dinámicas.

$.injectCSS({
    "#test": {
        height: 123
    }
});
Yako
fuente
similar a stackoverflow.com/questions/1212500/… .
user1742529
7

YUI tiene, con mucho, la mejor utilidad de hoja de estilo que he visto. Os animo a echarle un vistazo, pero he aquí una muestra:

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

Obviamente, hay otras formas mucho más simples de cambiar los estilos sobre la marcha, como las sugeridas aquí. Si tienen sentido para su problema, podrían ser mejores, pero definitivamente hay razones por las cuales modificar CSS es una mejor solución. El caso más obvio es cuando necesita modificar una gran cantidad de elementos. El otro caso importante es si necesita que sus cambios de estilo involucren a la cascada. Usar el dom para modificar un elemento siempre tendrá una prioridad más alta. Es el enfoque de mazo y es equivalente a usar el atributo de estilo directamente en el elemento html. Ese no es siempre el efecto deseado.

Russell Leggett
fuente
5

A partir de IE 9. Ahora puede cargar un archivo de texto y establecer una propiedad style.innerHTML. Entonces, esencialmente ahora puede cargar un archivo css a través de ajax (y obtener la devolución de llamada) y luego simplemente establecer el texto dentro de una etiqueta de estilo como esta.

Esto funciona en otros navegadores, no estoy seguro de qué tan atrás. Pero mientras no necesite soportar IE8, funcionaría.

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

y luego puede hacer que extraiga un archivo externo como myCss.css

.myClass { background:#F00; }
Codeguy
fuente
5

Aquí está la solución de Vishwanath ligeramente reescrita con comentarios:

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

Un proyecto interesante que podría ayudarlo en su tarea es JSS .

JSS es una mejor abstracción sobre CSS. Utiliza JavaScript como lenguaje para describir estilos de manera declarativa y sostenible. Es un compilador JS a CSS de alto rendimiento que funciona en tiempo de ejecución en los navegadores y en el lado del servidor.

La biblioteca JSS le permite inyectar en la sección DOM / head usando la .attach()función.

Repl versión en línea para evaluación.

Más información sobre JSS .

Un ejemplo:

// Use plugins.
jss.use(camelCase())

// Create your style.
const style = {
  myButton: {
    color: 'green'
  }
}

// Compile styles, apply plugins.
const sheet = jss.createStyleSheet(style)

// If you want to render on the client, insert it into DOM.
sheet.attach()
GibboK
fuente
3

Usando el cierre de google:

puedes usar el módulo ccsom:

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

El código javascript intenta ser un navegador cruzado al colocar el nodo CSS en el encabezado del documento.

Joe Heyming
fuente
3

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

Una opción para crear y actualizar dinámicamente la clase CSS en JavaScript:

  • Usando Style Element para crear una sección CSS
  • Usando una ID para el elemento de estilo para que podamos actualizar la
    clase 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)
Razan Paul
fuente
1

Revisé las respuestas y falta lo más obvio y directo: utilícelo document.write()para escribir una porción de CSS que necesita.

Aquí hay un ejemplo (verlo en codepen: http://codepen.io/ssh33/pen/zGjWga ):

<style>
   @import url(http://fonts.googleapis.com/css?family=Open+Sans:800);
   .d, body{ font: 3vw 'Open Sans'; padding-top: 1em; }
   .d {
       text-align: center; background: #aaf;
       margin: auto; color: #fff; overflow: hidden; 
       width: 12em; height: 5em;
   }
</style>

<script>
   function w(s){document.write(s)}
   w("<style>.long-shadow { text-shadow: ");
   for(var i=0; i<449; i++) {
      if(i!= 0) w(","); w(i+"px "+i+"px #444");
   }
   w(";}</style>");
</script> 

<div class="d">
    <div class="long-shadow">Long Shadow<br> Short Code</div>
</div>
Apilar
fuente
Esto está bien a menos que necesite crear reglas CSS después de cargar la página o esté usando XHTML.
Tim Down
1
function createCSSClass(selector, style, hoverstyle) 
{
    if (!document.styleSheets) 
    {
        return;
    }

    if (document.getElementsByTagName("head").length == 0) 
    {

        return;
    }
    var stylesheet;
    var mediaType;
    if (document.styleSheets.length > 0) 
    {
        for (i = 0; i < document.styleSheets.length; 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];
        }

        var media = styleSheet.media;
        mediaType = typeof media;
    }

    if (mediaType == "string") {
        for (i = 0; i < styleSheet.rules.length; i++) 
        {
            if (styleSheet.rules[i].selectorText.toLowerCase() == selector.toLowerCase()) 
            {
                styleSheet.rules[i].style.cssText = style;
                return;
            }
        }

        styleSheet.addRule(selector, style);
    }
    else if (mediaType == "object") 
    {
        for (i = 0; i < styleSheet.cssRules.length; i++) 
        {
            if (styleSheet.cssRules[i].selectorText.toLowerCase() == selector.toLowerCase()) 
            {
                styleSheet.cssRules[i].style.cssText = style;
                return;
            }
        }

        if (hoverstyle != null) 
        {
            styleSheet.insertRule(selector + "{" + style + "}", 0);
            styleSheet.insertRule(selector + ":hover{" + hoverstyle + "}", 1);
        }
        else 
        {
            styleSheet.insertRule(selector + "{" + style + "}", 0);
        }
    }
}





createCSSClass(".modalPopup  .header",
                                 " background-color: " + lightest + ";" +
                                  "height: 10%;" +
                                  "color: White;" +
                                  "line-height: 30px;" +
                                  "text-align: center;" +
                                  " width: 100%;" +
                                  "font-weight: bold; ", null);
tushar
fuente
¿Qué pasa si no hay una hoja de estilo actual en el documento
bluejayke
1

Aquí está mi solución modular:

var final_style = document.createElement('style');
final_style.type = 'text/css';

function addNewStyle(selector, style){
  final_style.innerHTML += selector + '{ ' + style + ' } \n';
};

function submitNewStyle(){
  document.getElementsByTagName('head')[0].appendChild(final_style);

  final_style = document.createElement('style');
  final_style.type = 'text/css';
};

function submitNewStyleWithMedia(mediaSelector){
  final_style.innerHTML = '@media(' + mediaSelector + '){\n' + final_style.innerHTML + '\n};';
    submitNewStyle();
};

Básicamente, en cualquier parte de su código, haga:,
addNewStyle('body', 'color: ' + color1);donde color1se define la variable.

Cuando desea "publicar" el archivo CSS actual, simplemente lo hace submitNewStyle(),
y luego puede agregar más CSS más adelante.

Si desea agregarlo con "consultas de medios", tiene la opción.
Después de "agregar nuevos estilos" simplemente usa submitNewStyleWithMedia('min-width: 1280px');.


Fue bastante útil para mi caso de uso, ya que estaba cambiando el CSS del sitio web público (no el mío) de acuerdo con la hora actual. Presento un archivo CSS antes de usar scripts "activos", y el resto después (hace que el sitio se vea un poco como debería antes de acceder a los elementos querySelector).

Gaben
fuente
Voy a probar esto hoy. Le haré saber cómo funciona esto en mi caso de uso. ¡¡¡¡Dedos cruzados!!!!
lopezdp
0

En beneficio de los buscadores; Si está utilizando jQuery, puede hacer lo siguiente:

var currentOverride = $('#customoverridestyles');

if (currentOverride) {
 currentOverride.remove();
}

$('body').append("<style id=\"customoverridestyles\">body{background-color:pink;}</style>");

Obviamente, puedes cambiar el CSS interno a lo que quieras.

Aprecio que algunas personas prefieran JavaScript puro, pero funciona y ha sido bastante robusto para escribir / sobrescribir estilos dinámicamente.

HockeyJ
fuente
0

Estaba buscando algunas de las respuestas aquí, y no pude encontrar nada que agregue automáticamente una nueva hoja de estilo si no hay ninguna, y si no, simplemente modifica una existente que ya contiene el estilo necesario, así que hice una nueva función ( debería funcionar en todos los navegadores, aunque no se haya probado, usa addRule y además de eso solo JavaScript nativo básico, avíseme si funciona):

function myCSS(data) {
    var head = document.head || document.getElementsByTagName("head")[0];
    if(head) {
        if(data && data.constructor == Object) {
            for(var k in data) {
                var selector = k;
                var rules = data[k];

                var allSheets = document.styleSheets;
                var cur = null;

                var indexOfPossibleRule = null,
                    indexOfSheet = null;
                for(var i = 0; i < allSheets.length; i++) {
                    indexOfPossibleRule = findIndexOfObjPropInArray("selectorText",selector,allSheets[i].cssRules);
                    if(indexOfPossibleRule != null) {
                        indexOfSheet = i;
                        break;
                    }
                }

                var ruleToEdit = null;
                if(indexOfSheet != null) {

                    ruleToEdit = allSheets[indexOfSheet].cssRules[indexOfPossibleRule];

                } else {
                    cur = document.createElement("style");
                    cur.type =  "text/css";
                    head.appendChild(cur);
                    cur.sheet.addRule(selector,"");
                    ruleToEdit = cur.sheet.cssRules[0];
                    console.log("NOPE, but here's a new one:", cur);
                }
                applyCustomCSSruleListToExistingCSSruleList(rules, ruleToEdit, (err) => {
                    if(err) {
                        console.log(err);
                    } else {
                        console.log("successfully added ", rules, " to ", ruleToEdit);
                    }
                });
            }
        } else {
            console.log("provide one paramter as an object containing the cssStyles, like: {\"#myID\":{position:\"absolute\"}, \".myClass\":{background:\"red\"}}, etc...");
        }
    } else {
        console.log("run this after the page loads");
    }

};  

luego solo agregue estas 2 funciones auxiliares dentro de la función anterior o en cualquier otro lugar:

function applyCustomCSSruleListToExistingCSSruleList(customRuleList, existingRuleList, cb) {
    var err = null;
    console.log("trying to apply ", customRuleList, " to ", existingRuleList);
    if(customRuleList && customRuleList.constructor == Object && existingRuleList && existingRuleList.constructor == CSSStyleRule) {
        for(var k in customRuleList) {
            existingRuleList["style"][k] = customRuleList[k];
        }

    } else {
        err = ("provide first argument as an object containing the selectors for the keys, and the second argument is the CSSRuleList to modify");
    }
    if(cb) {
        cb(err);
    }
}

function findIndexOfObjPropInArray(objPropKey, objPropValue, arr) {
    var index = null;
    for(var i = 0; i < arr.length; i++) {
        if(arr[i][objPropKey] == objPropValue) {
            index = i;
            break;
        }
    }
    return index;
}

(tenga en cuenta que en ambos uso un bucle for en lugar de .filter, ya que las clases de lista de estilo / regla CSS solo tienen una propiedad de longitud y ningún método .filter).

Entonces para llamarlo:

myCSS({
    "#coby": {
        position:"absolute",
        color:"blue"
    },
    ".myError": {
        padding:"4px",
        background:"salmon"
    }
})

Avíseme si funciona para su navegador o si produce un error.

bluejayke
fuente