Как динамически создать класс CSS в JavaScript и применить?


299

Мне нужно динамически создать класс таблицы стилей CSS в JavaScript и назначить его некоторым элементам HTML, таким как - div, table, span, tr и т. Д., А также некоторым элементам управления, таким как asp: Textbox, Dropdownlist и datalist.

Является ли это возможным?

Было бы неплохо с образцом.


2
взгляните на github.com/Box9/jss
jedierikb

Ответы:


394

Хотя я не уверен, почему вы хотите создавать CSS-классы с помощью JavaScript, вот вариант:

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
Мой вариант использования - это букмарклет, который выделяет определенные элементы в целях обеспечения качества.
TomG

25
Я уверен, что это приведет к неизвестной ошибке во время выполнения в IE 8 и менее.
Энди Хьюм

1
Мой вариант использования - загрузка случайного веб-шрифта Google, а затем присвоение классу fontFam класса randomFont :-)
w00t

26
Другой вариант использования - это когда вам нужна единая библиотека JS без зависимостей от файлов CSS. В моем случае я хочу легкие всплывающие окна оповещения стиля рычага из коробки.
xeolabs

1
Я делаю что-то подобное, как w00t. Я работаю над интерактивным html5-приложением, которое будет писать на холсте, и я хочу позволить своему пользователю выбирать из широкого спектра шрифтов для использования. Вместо того, чтобы иметь более полный CSS со всеми шрифтами, я планирую создать бэкэнд, в который я буду просто загружать данные шрифта, и всякий раз, когда программа загружается, небольшой вызов
веб-службы

117

Нашел лучшее решение, которое работает во всех браузерах.
Использует document.styleSheet для добавления или замены правил. Принятый ответ короток и удобен, но это работает через IE8 и менее.

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

Функция используется следующим образом.

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

2
Подтвердили работу с IE8. Мне действительно нужно было добавить "styleSheet.cssRules [i] .selectorText &&" и "styleSheet.rules [i] .selectorText &&" в цикл for MediaType ifs, потому что он не работал в Chrome, по-видимому, иногда selectorText отсутствует не определены.
2012 г.

@ w00t Не могли бы вы вставить или отредактировать код, чтобы он работал?
Hengjie

Я только что открыл Chrome (версия 34.0.1847.132), вставил функции и выполнил их, но это не сработало: «TypeError: Невозможно прочитать свойство« length »из null». Может ли быть так, что не получается создать его из консоли разработчика?
днуске

Оказывается, в некоторых версиях chrome (или chromium) не разрешается вставлять RULE для индекса 0. Вот исправление: styleSheet.insertRule (selector + "{" + style + "}", styleSheet.cssRules.length);
днуске

1
@dnuske Я столкнулся с той же проблемой. оказывается, что styleSheet.cssRules оценивается как ноль. Я использовал исправление, чтобы создать новую переменную var styleSheetLength = styleSheet.cssRules ? styleSheet.cssRules.length : 0и заменить ее использование на реализацию функции.
Радж Натани

27

Короткий ответ, это совместимо "во всех браузерах" (в частности, 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;");

И этот последний бит применяет класс к элементу:

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

Вот небольшая тестовая страница: https://gist.github.com/shadybones/9816763

Ключевым моментом является тот факт, что элементы стиля имеют свойство «styleSheet» / «sheet», которое можно использовать для добавления / удаления правил.


так что это создает новый элемент "стиль" при каждом создании класса? Так что, если бы я должен был создать 1000+ классов в цикле for на основе данных, это должно было бы применить document.head.appendChild 1000 раз?
блюджайке

для меня в chrome style.sheet и style.styleSheet не существует
bluejayke

17

Есть легкий плагин jQuery, который позволяет генерировать объявления CSS: jQuery-injectCSS

На самом деле, он использует JSS (CSS описан JSON), но его довольно легко обрабатывать, чтобы генерировать динамические таблицы стилей CSS.

$.injectCSS({
    "#test": {
        height: 123
    }
});


7

У YUI на данный момент лучшая утилита стилей, которую я когда-либо видел. Я призываю вас проверить это, но вот вкус:

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

Очевидно, есть другие, гораздо более простые способы изменения стилей на лету, такие как предложенные здесь. Если они имеют смысл для вашей проблемы, они могут быть лучшими, но есть определенные причины, по которым изменение css является лучшим решением. Наиболее очевидный случай - когда вам нужно изменить большое количество элементов. Другой важный случай - если вам нужны изменения стиля, чтобы задействовать каскад. Использование dom для изменения элемента всегда будет иметь более высокий приоритет. Его подход кувалдой и эквивалентен использованию атрибута стиля непосредственно в элементе html. Это не всегда желаемый эффект.


5

Начиная с IE 9. Теперь вы можете загрузить текстовый файл и установить свойство style.innerHTML. По сути, теперь вы можете загрузить файл CSS через ajax (и получить обратный вызов), а затем просто установить текст внутри тега стиля следующим образом.

Это работает в других браузерах, не уверен, как далеко назад. Но пока вам не нужно поддерживать IE8, тогда это будет работать.

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

и тогда вы можете получить его внешний файл, например, myCss.css

.myClass { background:#F00; }

5

Вот решение Вишваната, слегка переписанное с комментариями:

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 .

JSS - лучшая абстракция над CSS. Он использует JavaScript как язык для описания стилей декларативным и понятным способом. Это высокопроизводительный JS to CSS компилятор, который работает во время выполнения в браузерах и на стороне сервера.

Библиотека JSS позволяет вводить в раздел DOM / head, используя .attach()функцию.

Repl онлайн-версия для оценки.

Дополнительная информация о JSS .

Пример:

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

Использование Google Closure:

Вы можете просто использовать модуль ccsom:

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

Код javascript пытается быть кроссбраузерным при размещении узла css в заголовке документа.


3

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

Один из вариантов динамического создания и обновления CSS-класса в JavaScript:

  • Использование элемента стиля для создания раздела CSS
  • Использование идентификатора для элемента стиля, чтобы мы могли обновить
    класс 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

Просматривал ответы, и самое очевидное и прямое отсутствует: используйте, document.write()чтобы написать кусок CSS, который вам нужен.

Вот пример (посмотрите на 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>

Это нормально, если вам не нужно создавать правила CSS после загрузки страницы или использовать XHTML.
Тим Даун

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

Что
делать,

1

Вот мое модульное решение:

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

Вы в основном где-нибудь в своем коде делаете:,
addNewStyle('body', 'color: ' + color1);где color1определена переменная.

Если вы хотите «опубликовать» текущий CSS-файл, вы просто делаете это submitNewStyle(),
и тогда вы все равно можете добавить еще CSS.

Если вы хотите добавить его с помощью «медиа-запросов», у вас есть возможность.
После «добавления новых стилей» вы просто используете submitNewStyleWithMedia('min-width: 1280px');.


Это было довольно полезно для моего варианта использования, так как я менял CSS публичного (не моего) сайта в соответствии с текущим временем. Я отправляю один CSS-файл перед использованием «активных» сценариев, а остальные - после него (чтобы сайт выглядел примерно так, как должен до доступа к элементам через querySelector).


Я попробую это сегодня. Даст вам знать, как это работает в моем случае использования. Скрещенные пальцы!!!!
lopezdp

0

В интересах искателей; если вы используете jQuery, вы можете сделать следующее:

var currentOverride = $('#customoverridestyles');

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

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

Очевидно, вы можете изменить внутренний CSS на то, что вы хотите.

Признаться, некоторые люди предпочитают чистый JavaScript, но он работает и был достаточно надежным для динамического написания / перезаписи стилей.


0

Я просматривал некоторые ответы здесь, и я не смог найти ничего, что автоматически добавляет новую таблицу стилей, если ее нет, а если нет, то просто изменяет существующую таблицу, которая уже содержит необходимый стиль, поэтому я сделал новую функцию ( должен работать во всех браузерах, хотя и не тестировался, использует addRule и, кроме того, только базовый нативный JavaScript, дайте мне знать, если он работает):

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

};  

затем просто добавьте эти 2 вспомогательные функции либо внутри вышеуказанной функции, либо в любом другом месте:

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

(обратите внимание, что в обоих из них я использую цикл for вместо .filter, поскольку у классов стилей / списка правил CSS есть только свойство length, а метода .filter нет.)

Затем назвать это:

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

Дайте мне знать, если он работает для вашего браузера или выдает ошибку.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.