Comment créer dynamiquement une classe CSS en JavaScript et l'appliquer?


299

J'ai besoin de créer une classe de feuille de style CSS dynamiquement en JavaScript et de l'assigner à certains éléments HTML comme - div, table, span, tr, etc. et à certains contrôles comme asp: Textbox, Dropdownlist et datalist.

C'est possible?

Ce serait bien avec un échantillon.


2
jetez un oeil sur github.com/Box9/jss
jedierikb

Réponses:


394

Bien que je ne sache pas pourquoi vous souhaitez créer des classes CSS avec JavaScript, voici une option:

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
Mon cas d'utilisation est un bookmarklet qui met en évidence certains éléments à des fins d'assurance qualité.
TomG

25
À peu près sûr, cela entraîne une erreur d'exécution inconnue dans IE 8 et moins.
Andy Hume

1
Mon cas d'utilisation charge une police Web Google aléatoire, puis donne à la classe randomFont la famille de polices :-)
w00t

26
Un autre cas d'utilisation serait celui où vous voulez une seule bibliothèque JS sans dépendances sur les fichiers CSS. Dans mon cas, je veux des popups d'alerte légers de style grondement prêts à l'emploi.
xeolabs

1
Je fais quelque chose de similaire comme w00t. Je travaille sur une application html5 interactive, qui aura une écriture sur un canevas, et je veux laisser mon utilisateur choisir parmi une large gamme de polices à utiliser. Plutôt que d'avoir un loooong css avec toutes les polices, je prévois de créer un backend où je vais juste télécharger les données de police et chaque fois que le programme est chargé, un petit appel à un webservice apporte la police et les ajoute
CJLopez

117

Trouvé une meilleure solution, qui fonctionne sur tous les navigateurs.
Utilise document.styleSheet pour ajouter ou remplacer des règles. La réponse acceptée est courte et pratique mais cela fonctionne sur IE8 et moins aussi.

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 fonction est utilisée comme suit.

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

2
Travail confirmé avec IE8. J'ai dû ajouter un "styleSheet.cssRules [i] .selectorText &&" et "styleSheet.rules [i] .selectorText &&" dans les ifs de la boucle mediaType car cela ne fonctionnait pas dans Chrome, apparemment parfois le sélecteurText n'est pas pas défini.
w00t

@ w00t Pourriez-vous s'il vous plaît coller ou modifier le code pour le faire fonctionner?
Hengjie

Je viens d'ouvrir le Chrome (Version 34.0.1847.132), de coller les fonctions et de l'exécuter, mais cela n'a pas fonctionné: "TypeError: Impossible de lire la propriété 'length' de null". Se peut-il que cela ne fonctionne pas en le créant à partir de la console du développeur?
dnuske

Il s'avère que certaines versions de chrome (ou chrome) ne permettent pas d'insérer la règle sur l'index 0. Voici le correctif: styleSheet.insertRule (sélecteur + "{" + style + "}", styleSheet.cssRules.length);
dnuske

1
@dnuske J'ai rencontré le même problème. il s'avère que styleSheet.cssRules est évalué à null. le correctif que j'ai utilisé consiste à créer une nouvelle variable var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0et à substituer son utilisation à l'implémentation de la fonction.
Raj Nathani

27

Réponse courte, elle est compatible "sur tous les navigateurs" (spécifiquement 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;");

Et ce dernier bit applique la classe à un élément:

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

Voici également une petite page de test: https://gist.github.com/shadybones/9816763

La clé est le fait que les éléments de style ont une propriété "styleSheet" / "sheet" que vous pouvez utiliser pour ajouter / supprimer des règles.


donc cela crée un nouvel élément "style" à chaque création de classe? Donc, si je devais créer plus de 1000 classes dans une boucle for basée sur les données, cela devrait appliquer 1000 fois document.head.appendChild?
bluejayke

pour moi en chrome style.sheet et style.styleSheet n'existe pas
bluejayke


7

YUI a de loin le meilleur utilitaire de feuille de style que j'ai vu. Je vous encourage à le vérifier, mais voici un avant-goût:

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

Il existe évidemment d'autres façons beaucoup plus simples de changer de style à la volée, comme celles suggérées ici. S'ils ont un sens pour votre problème, ils pourraient être meilleurs, mais il y a certainement des raisons pour lesquelles la modification de css est une meilleure solution. Le cas le plus évident est celui où vous devez modifier un grand nombre d'éléments. L'autre cas majeur est si vous avez besoin de vos changements de style pour impliquer la cascade. L'utilisation du dom pour modifier un élément aura toujours une priorité plus élevée. C'est l'approche du marteau et équivaut à utiliser l'attribut style directement sur l'élément html. Ce n'est pas toujours l'effet souhaité.


5

Depuis IE 9. Vous pouvez maintenant charger un fichier texte et définir une propriété style.innerHTML. Donc, essentiellement, vous pouvez maintenant charger un fichier css via ajax (et obtenir le rappel), puis définir simplement le texte à l'intérieur d'une balise de style comme celle-ci.

Cela fonctionne dans d'autres navigateurs, je ne sais pas à quelle distance. Mais tant que vous n'avez pas besoin de prendre en charge IE8, cela fonctionnera.

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

et puis vous pouvez le faire extraire un fichier externe comme myCss.css

.myClass { background:#F00; }

5

Voici la solution de Vishwanath légèrement réécrite avec des commentaires:

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

JSS est un projet intéressant qui pourrait vous aider dans votre tâche .

JSS est une meilleure abstraction que CSS. Il utilise JavaScript comme langage pour décrire les styles de manière déclarative et maintenable. Il s'agit d'un compilateur JS vers CSS hautes performances qui fonctionne au moment de l'exécution dans les navigateurs et côté serveur.

La bibliothèque JSS vous permet d'injecter dans la section DOM / head en utilisant la .attach()fonction.

Repl version en ligne pour évaluation.

Plus d' informations sur JSS .

Un exemple:

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

Utilisation de la fermeture de Google:

vous pouvez simplement utiliser le module ccsom:

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

Le code javascript tente d'être cross-browser lors de la mise en place du nœud CSS dans l'en-tête du document.


3

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

Une option pour créer et mettre à jour dynamiquement une classe CSS en JavaScript:

  • Utilisation de l'élément de style pour créer une section CSS
  • Utiliser un ID pour l'élément style afin que nous puissions mettre à jour 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

Regardé à travers les réponses et le plus évident et le plus simple est manquant: utilisez document.write() pour écrire un morceau de CSS dont vous avez besoin.

Voici un exemple (regardez-le sur 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>

C'est très bien, sauf si vous devez créer des règles CSS après le chargement de la page ou utilisez 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);

et s'il n'y a pas de feuille de style actuelle sur le document
bluejayke

1

Voici ma solution modulaire:

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

Vous essentiellement partout dans votre code faites:
addNewStyle('body', 'color: ' + color1);, oùcolor1 est définie la variable.

Lorsque vous souhaitez "publier" le fichier CSS actuel que vous faites submitNewStyle(),
puis vous pouvez encore ajouter plus de CSS plus tard.

Si vous souhaitez l'ajouter avec des "requêtes multimédias", vous avez la possibilité.
Après "ajout de nouveaux styles", vous utilisez simplement submitNewStyleWithMedia('min-width: 1280px');.


C'était assez utile pour mon cas d'utilisation, car je modifiais le CSS du site Web public (pas le mien) en fonction de l'heure actuelle. Je soumets un fichier CSS avant d'utiliser des scripts "actifs", et le reste après (donne au site un aspect similaire à ce qu'il devrait avant d'accéder aux éléments via querySelector).


Je vais essayer ça aujourd'hui. Vous permettra de savoir comment cela fonctionne dans mon cas d'utilisation. Doigts croisés!!!!
lopezdp

0

Pour le bénéfice des chercheurs; si vous utilisez jQuery, vous pouvez effectuer les opérations suivantes:

var currentOverride = $('#customoverridestyles');

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

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

Évidemment, vous pouvez changer le CSS interne en ce que vous voulez.

Appréciez que certaines personnes préfèrent le JavaScript pur, mais cela fonctionne et a été assez robuste pour l'écriture / le remplacement dynamique des styles.


0

Je regardais certaines des réponses ici, et je n'ai rien trouvé qui ajoute automatiquement une nouvelle feuille de style s'il n'y en a pas, et si ce n'est pas simplement en modifiant une existante qui contient déjà le style nécessaire, j'ai donc créé une nouvelle fonction ( devrait fonctionner sur tous les navigateurs, bien qu'il ne soit pas testé, utilise addRule et, en plus, seul le JavaScript natif de base, faites-moi savoir si cela fonctionne):

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

};  

puis ajoutez simplement ces 2 fonctions d'assistance soit à l'intérieur de la fonction ci-dessus, soit n'importe où ailleurs:

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

(notez que dans les deux, j'utilise une boucle for au lieu de .filter, car les classes de style / liste de règles CSS n'ont qu'une propriété length et aucune méthode .filter.)

Puis pour l'appeler:

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

Faites-moi savoir si cela fonctionne pour votre navigateur ou donne une erreur.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.