Como criar dinamicamente a classe CSS em JavaScript e aplicar?

299

Eu preciso criar uma classe de folha de estilo CSS dinamicamente em JavaScript e atribuí-la a alguns elementos HTML como - div, table, span, tr, etc e a alguns controles como asp: Textbox, Dropdownlist e datalist.

É possível?

Seria bom com uma amostra.

Himadri
fonte
2
dê uma olhada em github.com/Box9/jss
jedierikb
Veja também stackoverflow.com/questions/1212500/…
jantimon 7/12

Respostas:

394

Embora eu não saiba por que você deseja criar classes CSS com JavaScript, aqui está uma opção:

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
fonte
10
Meu caso de uso é um bookmarklet que destaca alguns elementos para fins de controle de qualidade.
TomG 29/04
25
Certamente isso resulta em um erro de tempo de execução desconhecido no IE 8 e menos.
Andy Hume
1
Meu caso de uso está a carregar uma fonte web aleatório Google e, em seguida, dando a classe randomFont o font-family :-)
w00t
26
Outro caso de uso seria onde você deseja uma única lib JS sem dependências nos arquivos CSS. No meu caso, quero pop-ups de alerta leves e de estilo rosnado prontos para uso.
precisa saber é o seguinte
1
Estou fazendo algo semelhante como w00t. Estou trabalhando em um aplicativo html5 interativo, que terá escrita em uma tela, e quero permitir que meu usuário selecione entre uma ampla variedade de fontes para usar. Em vez de ter um css loooong com toda a fonte, estou pensando em criar um back-end onde apenas carregarei os dados da fonte e sempre que o programa for carregado, uma pequena ligação para um serviço da web trará a fonte e os adicionará
CJLopez
117

Encontrou uma solução melhor, que funciona em todos os navegadores.
Usa document.styleSheet para adicionar ou substituir regras. A resposta aceita é curta e prática, mas também funciona no IE8 e menos.

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

A função é usada da seguinte maneira.

createCSSSelector('.mycssclass', 'display:none');
Vishwanath
fonte
2
Trabalho confirmado com o IE8. Eu tive que adicionar um "styleSheet.cssRules [i] .selectorText &&" e "styleSheet.rules [i] .selectorText &&" no loop for mediaType ifs porque não funcionava no Chrome, aparentemente às vezes o selectorText é está definido.
W00t
@ w00t Você pode colar ou editar o código para fazê-lo funcionar?
Hengjie 17/05
Acabei de abrir o Chrome (versão 34.0.1847.132) colei as funções e a executei, mas não funcionou: "TypeError: Não é possível ler a propriedade 'length' de null". Pode ser que não funcione criando-o no console do desenvolvedor?
Dnuske 4/14/14
Acontece que algumas versões do chrome (ou chromium) não permitem insertRule no índice 0. Aqui está a correção: styleSheet.insertRule (seletor + "{" + style + "}", styleSheet.cssRules.length);
Dnuske 5/05
1
@dnuske eu encontrei o mesmo problema. Acontece que styleSheet.cssRules é avaliado como nulo. a correção que usei é criar uma nova variável var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0e substituir seu uso pela implementação da função.
Raj Nathani
27

Resposta curta, isso é compatível "em todos os navegadores" (especificamente, 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 este bit final aplica a classe a um 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;
    }
}

Aqui está uma pequena página de teste também: https://gist.github.com/shadybones/9816763

O ponto principal é o fato de que os elementos de estilo têm uma propriedade "styleSheet" / "sheet" na qual você pode usar para adicionar / remover regras.

shadybones
fonte
então isso cria um novo elemento "estilo" a cada criação de classe? Então, se eu fosse criar mais de 1000 classes em um loop for com base em dados, seria necessário aplicar document.head.appendChild 1000 vezes?
bluejayke
para mim em cromo style.sheet e não style.styleSheet não existe
bluejayke
17

Existe um plugin jQuery leve que permite gerar declarações CSS: jQuery-injectCSS

De fato, ele usa JSS (CSS descrito por JSON), mas é bastante fácil de manusear para gerar folhas de estilo css dinâmicas.

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

YUI tem de longe o melhor utilitário de folha de estilo que eu já vi por aí. Convido você a conferir, mas aqui está uma amostra:

// 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, existem outras maneiras muito mais simples de mudar de estilo rapidamente, como as sugeridas aqui. Se eles fazem sentido para o seu problema, eles podem ser os melhores, mas definitivamente existem razões pelas quais modificar o css é uma solução melhor. O caso mais óbvio é quando você precisa modificar um grande número de elementos. O outro caso importante é se você precisa que suas mudanças de estilo envolvam a cascata. Usar o dom para modificar um elemento sempre terá uma prioridade mais alta. É a abordagem da marreta e é equivalente a usar o atributo style diretamente no elemento html. Esse nem sempre é o efeito desejado.

Russell Leggett
fonte
5

A partir do IE 9. Agora você pode carregar um arquivo de texto e definir uma propriedade style.innerHTML. Portanto, agora você pode carregar um arquivo css através do ajax (e obter o retorno de chamada) e apenas definir o texto dentro de uma tag de estilo como esta.

Isso funciona em outros navegadores, não sei ao certo quanto tempo atrás. Mas desde que você não precise dar suporte ao IE8, ele funcionaria.

// 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 então você pode puxar um arquivo externo como o myCss.css

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

Aqui está a solução de Vishwanath ligeiramente reescrita com comentários:

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;
    }
user3705905
fonte
4

Um projeto interessante que pode ajudá-lo em sua tarefa é o JSS .

JSS é uma melhor abstração sobre CSS. Ele usa JavaScript como uma linguagem para descrever estilos de forma declarativa e sustentável. É um compilador JS para CSS de alto desempenho que funciona em tempo de execução nos navegadores e no servidor.

A biblioteca JSS permite injetar na seção DOM / head usando a .attach()função

Substitua a versão online para avaliação.

Mais informações sobre JSS .

Um exemplo:

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

Usando o fechamento do google:

você pode simplesmente usar o módulo ccsom:

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

O código javascript tenta ser entre navegadores ao colocar o nó css no cabeçalho do documento.

Joe Heyming
fonte
3

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

Uma opção para criar e atualizar dinamicamente a classe CSS em JavaScript:

  • Usando Style Element para criar uma seção CSS
  • Usando um ID para o elemento style para que possamos atualizar a
    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)
Razan Paul
fonte
1

Observou as respostas e o mais óbvio e direto está faltando: use document.write()para escrever um pedaço de CSS necessário.

Aqui está um exemplo (veja no 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>
Pilha
fonte
Isso é bom, a menos que você precise criar regras CSS após o carregamento da página ou esteja usando XHTML.
Tim Baixo
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
fonte
o que se há de estilo nenhuma corrente no documento
bluejayke
1

Aqui está minha solução 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();
};

Você basicamente em qualquer lugar do seu código do:,
addNewStyle('body', 'color: ' + color1);where color1é a variável definida.

Quando você deseja "postar" o arquivo CSS atual, basta fazê -lo e submitNewStyle(), em
seguida, ainda pode adicionar mais CSS posteriormente.

Se você deseja adicioná-lo com "consultas de mídia", tem a opção
Após "adicionar novos estilos", você simplesmente usa submitNewStyleWithMedia('min-width: 1280px');.


Foi bastante útil para o meu caso de uso, pois eu estava alterando o CSS do site público (não o meu) de acordo com o horário atual. Eu envio um arquivo CSS antes de usar scripts "ativos" e o restante depois (faz o site parecer mais ou menos antes de acessar os elementos querySelector).

Gaben
fonte
Eu vou tentar isso hoje. Informaremos como isso funciona no meu caso de uso. Dedos cruzados!!!!
lopezdp
0

Para o benefício dos pesquisadores; se você estiver usando jQuery, poderá fazer o seguinte:

var currentOverride = $('#customoverridestyles');

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

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

Obviamente, você pode alterar o css interno para o que quiser.

Entendo que algumas pessoas preferem JavaScript puro, mas funciona e tem sido bastante robusto para escrever / substituir estilos dinamicamente.

HockeyJ
fonte
0

Eu estava examinando algumas das respostas aqui e não consegui encontrar nada que adicionasse automaticamente uma nova folha de estilo, se não houver, e se não simplesmente modificasse uma existente que já contivesse o estilo necessário, criei uma nova função ( deve funcionar em todos os navegadores, embora não testados, use addRule e, além disso, apenas o JavaScript nativo básico, deixe-me saber se 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");
    }

};  

basta adicionar estas 2 funções auxiliares dentro da função acima ou em qualquer outro 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;
}

(observe que, nos dois, eu uso um loop for em vez de .filter, pois as classes de lista de regras / estilo CSS têm apenas uma propriedade length e nenhum método .filter.)

Então, para chamá-lo:

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

Deixe-me saber se ele funciona no seu navegador ou se apresenta um erro.

bluejayke
fonte