Come creare dinamicamente una classe CSS in JavaScript e applicare?


Risposte:


394

Anche se non sono sicuro del motivo per cui desideri creare classi CSS con JavaScript, ecco un'opzione:

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

10
Il mio caso d'uso è un bookmarklet che evidenzia alcuni elementi ai fini del QA.
TomG

25
Abbastanza sicuro questo provoca un errore di runtime sconosciuto in IE 8 e meno.
Andy Hume,

1
Il mio caso d'uso sta caricando un carattere web di Google casuale e quindi dando alla classe randomFont la famiglia di caratteri :-)
w00t

26
Un altro caso d'uso sarebbe dove si desidera una singola libreria JS senza dipendenze dai file CSS. Nel mio caso, voglio immediatamente popup di avviso in stile ringhio leggero.
Xeolab

1
Sto facendo qualcosa di simile come w00t. Sto lavorando a un'app html5 interattiva, che avrà la scrittura su una tela, e voglio consentire al mio utente di selezionare tra una vasta gamma di caratteri da utilizzare. Piuttosto che avere un css loooong con tutto il font, sto progettando di creare un backend in cui caricherò solo i dati del font e ogni volta che il programma viene caricato, una piccola chiamata a un servizio web porta il font e li aggiunge
CJLopez

117

Ho trovato una soluzione migliore, che funziona su tutti i browser.
Utilizza document.styleSheet per aggiungere o sostituire regole. La risposta accettata è breve e utile, ma funziona su IE8 e anche meno.

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 funzione è utilizzata come segue.

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

2
Confermato il lavoro con IE8. Ho dovuto aggiungere un "styleSheet.cssRules [i] .selectorText &&" e "styleSheet.rules [i] .selectorText &&" nel mediaType for-loop ifs perché non funzionava in Chrome, apparentemente a volte il selectorText non è non definito.
wgt

@ w00t Potresti incollare o modificare il codice per farlo funzionare?
Hengjie,

Ho appena aperto Chrome (versione 34.0.1847.132) incollato le funzioni ed eseguito, ma non ha funzionato: "TypeError: Impossibile leggere la proprietà 'lunghezza' di null". Può essere che non funzioni creando dalla console degli sviluppatori?
dnuske,

Risulta che alcune versioni di chrome (o chromium) non consentono di inserire la regola sull'indice 0. Ecco la correzione: styleSheet.insertRule (selettore + "{" + style + "}", styleSheet.cssRules.length);
dnuske,

1
@dnuske Ho riscontrato lo stesso problema. si scopre che styleSheet.cssRules viene valutato come nullo. la correzione che ho usato è creare una nuova variabile var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0e sostituirne l'utilizzo rispetto all'implementazione della funzione.
Raj Nathani,

27

Risposta breve, questo è compatibile "su tutti i browser" (in particolare, 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;");

E questo bit finale applica la classe 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;
    }
}

Ecco anche una piccola pagina di prova: https://gist.github.com/shadybones/9816763

La chiave è il fatto che gli elementi di stile hanno una proprietà "styleSheet" / "sheet" su cui è possibile aggiungere / rimuovere le regole.


quindi questo crea un nuovo elemento "stile" ogni creazione di classe? Quindi, se dovessi creare più di 1000 classi in un ciclo for in base ai dati, questo dovrebbe applicare document.head.appendChild 1000 volte?
Bluejayke,

per me in chrome style.sheet e style.styleSheet non esiste
bluejayke


7

YUI ha di gran lunga la migliore utility per fogli di stile che abbia mai visto. Ti incoraggio a dare un'occhiata, ma ecco un assaggio:

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

Ci sono ovviamente altri modi molto più semplici di cambiare stile al volo come quelli suggeriti qui. Se hanno senso per il tuo problema, potrebbero essere i migliori, ma ci sono sicuramente dei motivi per cui modificare CSS è una soluzione migliore. Il caso più ovvio è quando è necessario modificare un gran numero di elementi. L'altro caso importante è se hai bisogno dei cambiamenti di stile per coinvolgere la cascata. L'uso di dom per modificare un elemento avrà sempre una priorità più alta. È l'approccio della mazza ed equivale a usare l'attributo style direttamente sull'elemento html. Questo non è sempre l'effetto desiderato.


5

A partire da IE 9. Ora puoi caricare un file di testo e impostare una proprietà style.innerHTML. Quindi essenzialmente puoi ora caricare un file CSS tramite Ajax (e ottenere il callback) e quindi semplicemente impostare il testo all'interno di un tag di stile come questo.

Funziona con altri browser, non sono sicuro di quanto tempo fa. Ma fino a quando non è necessario supportare IE8, funzionerebbe.

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

e quindi puoi farlo estrarre un file esterno come myCss.css

.myClass { background:#F00; }

5

Ecco la soluzione di Vishwanath leggermente riscritta con commenti:

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

4

Un progetto interessante che potrebbe aiutarti nella tua attività è JSS .

JSS è un'astrazione migliore rispetto ai CSS. Utilizza JavaScript come linguaggio per descrivere gli stili in modo dichiarativo e mantenibile. È un compilatore da JS a CSS ad alte prestazioni che funziona in fase di esecuzione nei browser e sul lato server.

La libreria JSS ti permette di iniettare nella sezione DOM / head usando la .attach()funzione.

Sostituisci la versione online per la valutazione.

Ulteriori informazioni su JSS .

Un esempio:

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

3

Utilizzando la chiusura di Google:

puoi semplicemente usare il modulo ccsom:

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

Il codice JavaScript tenta di essere cross-browser quando si inserisce il nodo CSS nell'intestazione del documento.


3

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

Un'opzione per creare e aggiornare dinamicamente la classe CSS in JavaScript:

  • Utilizzo di Style Element per creare una sezione CSS
  • Utilizzo di un ID per l'elemento style in modo da poter aggiornare la
    classe 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)

1

Guardato attraverso le risposte e manca il più ovvio e diretto: usa document.write()per scrivere un pezzo di CSS che ti serve.

Ecco un esempio (visualizzalo su 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>

Questo va bene a meno che non sia necessario creare regole CSS dopo il caricamento della pagina o utilizzare 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);

e se non ci fosse un foglio di stile attuale sul documento
bluejayke,

1

Ecco la mia soluzione modulare:

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

Praticamente ovunque nel tuo codice fai:,
addNewStyle('body', 'color: ' + color1);dove color1è definita variabile.

Quando vuoi "postare" il file CSS corrente, semplicemente submitNewStyle(),
e poi puoi ancora aggiungere altri CSS in seguito.

Se vuoi aggiungerlo con "media query", hai la possibilità.
Dopo aver aggiunto "NewStyles" devi semplicemente usare submitNewStyleWithMedia('min-width: 1280px');.


È stato piuttosto utile per il mio caso d'uso, poiché stavo cambiando CSS del sito Web pubblico (non mio) in base all'ora corrente. Prima di utilizzare gli script "attivi", invio un file CSS e poi il resto (fa sembrare il sito un po 'come dovrebbe prima di accedere agli elementi querySelector).


Lo proverò oggi. Ti farà sapere come funziona nel mio caso d'uso. Dita incrociate!!!!
Lopezdp,

0

A beneficio dei ricercatori; se stai usando jQuery, puoi fare quanto segue:

var currentOverride = $('#customoverridestyles');

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

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

Ovviamente puoi cambiare il CSS interno con quello che vuoi.

Apprezzare alcune persone preferiscono il puro JavaScript, ma funziona ed è stato abbastanza robusto per scrivere / sovrascrivere gli stili in modo dinamico.


0

Stavo esaminando alcune delle risposte qui e non riuscivo a trovare nulla che aggiungesse automaticamente un nuovo foglio di stile se non ce ne sono, e se non semplicemente modifica uno esistente che contiene già lo stile necessario, quindi ho creato una nuova funzione ( dovrebbe funzionare su tutti i browser, anche se non testato, utilizza addRule e oltre a questo solo JavaScript nativo di base, fammi sapere se funziona):

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

};  

quindi aggiungi semplicemente queste 2 funzioni di supporto all'interno della funzione sopra o in qualsiasi altro luogo:

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

(nota che in entrambi uso un ciclo for invece di .filter, dato che le classi CSS / style list list hanno solo una proprietà length e nessun metodo .filter.)

Quindi chiamarlo:

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

Fammi sapere se funziona per il tuo browser o dà un errore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.