Jak dynamicznie utworzyć klasę CSS w JavaScript i zastosować?

299

Muszę dynamicznie utworzyć klasę arkusza stylów CSS w JavaScript i przypisać ją do niektórych elementów HTML, takich jak - div, table, span, tr itp., Oraz do niektórych elementów sterujących, takich jak asp: Textbox, Dropdownlist i datalist.

Czy to możliwe?

Byłoby miło z próbką.

Himadri
źródło
2
spojrzeć na github.com/Box9/jss
jedierikb
Zobacz także stackoverflow.com/questions/1212500/…
jantimon,

Odpowiedzi:

394

Chociaż nie jestem pewien, dlaczego chcesz tworzyć klasy CSS za pomocą JavaScript, oto opcja:

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
źródło
10
Mój przypadek użycia to bookmarklet, który podkreśla niektóre elementy do celów kontroli jakości.
TomG
25
Jestem pewien, że powoduje to nieznany błąd czasu wykonywania w IE 8 i mniej.
Andy Hume,
1
Mój przypadek użycia polega na załadowaniu losowej czcionki internetowej Google, a następnie nadaniu klasie randomFont rodziny czcionek :-)
w00t
26
Innym przykładem użycia może być pojedyncza biblioteka JS bez zależności od plików CSS. W moim przypadku chcę, aby wyskakujące okienka ostrzegawcze w stylu lekkiego warczenia były gotowe.
xeolabs,
1
Robię coś podobnego jak w00t. Pracuję nad interaktywną aplikacją HTML5, która będzie miała napis na płótnie, i chcę pozwolić mojemu użytkownikowi wybierać spośród szerokiej gamy czcionek do użycia. Zamiast długiego css z całą czcionką, planuję utworzyć backend, w którym po prostu załaduję dane czcionki i za każdym razem, gdy program zostanie załadowany, małe wywołanie do serwisu internetowego przynosi czcionkę i dodaje je
CJLopez
117

Znaleziono lepsze rozwiązanie, które działa we wszystkich przeglądarkach.
Używa document.styleSheet do dodawania lub zastępowania reguł. Zaakceptowana odpowiedź jest krótka i przydatna, ale działa to również w IE8 i mniej.

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

Funkcja jest używana w następujący sposób.

createCSSSelector('.mycssclass', 'display:none');
Vishwanath
źródło
2
Potwierdzono pracę z IE8. Musiałem dodać „styleSheet.cssRules [i] .selectorText &&” i „styleSheet.rules [i] .selectorText &&” w pętli for mediaType, jeśli nie działa w Chrome, najwyraźniej czasami selektorTekst nie jest zdefiniowane.
w00t
@ w00t Czy możesz wkleić lub edytować kod, aby działał?
Hengjie
Właśnie otworzyłem Chrome (wersja 34.0.1847.132) wkleiłem funkcje i wykonałem je, ale to nie działało: „TypeError: Nie można odczytać właściwości„ length ”o wartości null”. Czy to możliwe, że nie działa tworzenie go z konsoli programisty?
dnuske
Okazuje się, że niektóre wersje chrome (lub chromu) nie pozwalają na wstawienie reguły w indeksie 0. Oto poprawka: styleSheet.insertRule (selektor + „{„ + style + ”}”, styleSheet.cssRules.length);
dnuske
1
@dnuske Spotkałem ten sam problem. okazuje się, że styleSheet.cssRules ocenia na zero. poprawka, której użyłem, polega na utworzeniu nowej zmiennej var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0i zastąpieniu jej użyciem implementacją funkcji.
Raj Nathani,
27

Krótka odpowiedź, jest kompatybilny „we wszystkich przeglądarkach” (w szczególności 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;");

I ten ostatni bit stosuje klasę do elementu:

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

Oto także mała strona testowa: https://gist.github.com/shadybones/9816763

Najważniejsze jest to, że elementy stylu mają właściwość „styleSheet” / „sheet”, za pomocą której można dodawać / usuwać reguły.

cienie kości
źródło
więc to tworzy nowy element „stylu” przy każdym tworzeniu klasy? Więc jeśli miałbym stworzyć ponad 1000 klas w pętli for na podstawie danych, musiałbym zastosować dokument.head.appendChild 1000 razy?
bluejayke
dla mnie w stylu chrome. arkusz i styl. arkusz styl nie istnieje
bluejayke
17

Istnieje lekka wtyczka jQuery, która pozwala generować deklaracje CSS: jQuery-injectCSS

W rzeczywistości używa JSS (CSS opisany przez JSON), ale jest dość łatwy w obsłudze, aby wygenerować dynamiczne arkusze stylów css.

$.injectCSS({
    "#test": {
        height: 123
    }
});
Yako
źródło
podobny do stackoverflow.com/questions/1212500/… .
user1742529,
7

YUI ma zdecydowanie najlepsze narzędzie arkuszy stylów , jakie widziałem. Zachęcam do sprawdzenia, ale oto smak:

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

Istnieją oczywiście inne, znacznie prostsze sposoby zmiany stylów w locie, takie jak te sugerowane tutaj. Jeśli mają sens dla twojego problemu, mogą być najlepsze, ale są zdecydowanie powody, dla których modyfikacja css jest lepszym rozwiązaniem. Najbardziej oczywistym przypadkiem jest modyfikacja dużej liczby elementów. Innym ważnym przypadkiem jest sytuacja, w której zmiany stylu wymagają kaskady. Użycie dom do zmodyfikowania elementu zawsze będzie miało wyższy priorytet. Jest to młot kowalski i jest równoważne z użyciem atrybutu stylu bezpośrednio na elemencie HTML. To nie zawsze jest pożądany efekt.

Russell Leggett
źródło
5

Począwszy od IE 9. Możesz teraz załadować plik tekstowy i ustawić właściwość style.innerHTML. Zasadniczo możesz teraz załadować plik css przez ajax (i uzyskać wywołanie zwrotne), a następnie po prostu ustawić tekst wewnątrz takiego stylu.

Działa to w innych przeglądarkach, nie wiem, jak daleko wstecz. Ale tak długo, jak nie potrzebujesz obsługiwać IE8, działałoby.

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

i wtedy możesz poprosić go o pobranie zewnętrznego pliku, takiego jak myCss.css

.myClass { background:#F00; }
Codeguy
źródło
5

Oto rozwiązanie Vishwanath nieco przepisane z komentarzami:

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;
    }
użytkownik3705905
źródło
4

Ciekawym projektem, który może ci pomóc w zadaniu, jest JSS .

JSS jest lepszą abstrakcją niż CSS. Używa JavaScript jako języka do opisywania stylów w sposób deklaratywny i łatwy do utrzymania. Jest to wysokowydajny kompilator JS na CSS, który działa w czasie wykonywania w przeglądarkach i po stronie serwera.

Biblioteka JSS umożliwia wstrzykiwanie do sekcji DOM / head za pomocą .attach()funkcji.

Zamień wersję online do oceny.

Więcej informacji o JSS .

Przykład:

// 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
źródło
3

Za pomocą zamknięcia google:

możesz po prostu użyć modułu ccsom:

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

Kod javascript próbuje przeniknąć przez przeglądarkę do węzła css w nagłówku dokumentu.

Joe Heyming
źródło
3

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

Jedna opcja dynamicznego tworzenia i aktualizowania klasy CSS w JavaScript:

  • Użycie elementu stylu do utworzenia sekcji CSS
  • Użycie identyfikatora dla elementu stylu, abyśmy mogli zaktualizować
    klasę 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
źródło
1

Przejrzałem odpowiedzi i brakuje najbardziej oczywistego i bezpośredniego: użyj, document.write()aby napisać potrzebną część CSS.

Oto przykład (zobacz go na 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>
Stos
źródło
Jest to w porządku, chyba że musisz utworzyć reguły CSS po załadowaniu strony lub używasz 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
źródło
co jeśli nie ma aktualnego arkusza stylów w dokumencie
bluejayke
1

Oto moje rozwiązanie modułowe:

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

Zasadniczo gdziekolwiek w kodzie robisz:,
addNewStyle('body', 'color: ' + color1);gdziecolor1 jest zdefiniowana zmienna.

Jeśli chcesz „opublikować” bieżący plik CSS, po prostu robisz submitNewStyle(),
a następnie nadal możesz dodać więcej CSS.

Jeśli chcesz dodać go do „zapytań o media”, masz taką opcję.
Po „dodaniu nowych stylów” po prostu używasz submitNewStyleWithMedia('min-width: 1280px');.


Było to bardzo przydatne w moim przypadku użycia, ponieważ zmieniałem CSS publicznej (nie mojej) strony internetowej zgodnie z bieżącym czasem. Przesyłam jeden plik CSS przed użyciem „aktywnych” skryptów, a resztę później (sprawia, że ​​strona wygląda trochę tak, jak powinna przed uzyskaniem dostępu do elementów querySelector).

Gaben
źródło
Spróbuję tego dzisiaj. Powiadomię Cię, jak to działa w moim przypadku użycia. Skrzyżowane palce!!!!
lopezdp
0

Z korzyścią dla poszukiwaczy; jeśli używasz jQuery, możesz wykonać następujące czynności:

var currentOverride = $('#customoverridestyles');

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

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

Oczywiście możesz zmienić wewnętrzny css na cokolwiek chcesz.

Doceń to, że niektórzy wolą czysty JavaScript, ale działa i jest dość niezawodny do dynamicznego pisania / nadpisywania stylów.

Hokej
źródło
0

Przeglądałem niektóre odpowiedzi tutaj i nie mogłem znaleźć niczego, co automatycznie dodałoby nowy arkusz stylów, jeśli go nie ma, a jeśli nie, po prostu modyfikuje istniejący, który zawiera już potrzebny styl, więc utworzyłem nową funkcję ( powinien działać we wszystkich przeglądarkach, choć nie został przetestowany, używa addRule, a poza tym tylko podstawowy natywny JavaScript, daj mi znać, jeśli to działa):

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

};  

następnie po prostu dodaj te 2 funkcje pomocnicze w ramach powyższej funkcji lub w dowolnym innym miejscu:

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

(zauważ, że w obu z nich używam pętli for zamiast .filter, ponieważ klasy listy stylów / reguł CSS mają tylko właściwość length i nie mają metody .filter).

Następnie nazwij to:

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

Daj mi znać, czy to działa w twojej przeglądarce lub daje błąd.

bluejayke
źródło