Селекторы групп CSSRules с общими свойствами

Этот script при включении в HTML-документ, который включает любые объявленные стили (исключая те, что установлен в style=""), выводит оптимизированную таблицу стилей на страницу. script использует следующие методы...

  • Игнорировать любые правила @ или :, чтобы оставить отзывчивые стили как есть.
  • Отделите правила в правилах с одним селектором, чтобы позже проверить наследование.
  • Удалить правила, которые применяются к селекторам, которых нет в документе, благодаря @RickHitchcock для этого.
  • Извлеките объявленные и вычисленные стили в одном формате (исключая проценты), затем сравните оба значения друг с другом, затем удалите свойство и снова проверьте.
  • если объявленное значение соответствует вычисленному значению, и, удалив свойство, значение изменится, установите флаг keep. Это говорит нам, влияет ли свойство на элемент, если ни одно из элементов не было затронуто этим свойством... удалите его.
  • Если в остальном CSSRule нет свойств, удалите это правило.
  • В качестве побочного эффекта большинство селекторов, которые не меняют настройки браузера по умолчанию, будут удалены (если не использовать font в отличие от font-* и т.п., которые активируют остальные настройки для этого свойства).

При запуске этого script на сайте, который включает стили, относящиеся к динамическим элементам, я просто переношу их в медиа-запрос

@media (min-width: 0px) {
    /* This says that these styles always apply */
}

Вопрос

Как я могу сгруппировать селектора с общими свойствами?

(Демо)

var stylesheets = document.styleSheets, stylesheet, i;
var ruleText = "";
if(stylesheets && stylesheets.length) {
    for (i = 0; (stylesheet = stylesheets[i]); i++) {
        var rules = stylesheet.rules, rule, j;
        if(rules && rules.length) {
            for (j = 0; (rule = rules[j]); j++) {
                if(rule.type === rule.STYLE_RULE) {
                    if(rule.selectorText.indexOf(',') >= 0) {
                        var newRules = [];
                        var selectors = rule.selectorText.split(','), selector, k;
                        for(k = 0; (selector = selectors[k]); k++) {
                            var styles = rule.style, style, l;
                            var elements = document.querySelectorAll(selector.trim()), element, l;
                            if(elements.length) {
                                var styleString = '';
                                for(m = 0; (style = styles[m]); m++) {
                                    styleString += style + ': ' + styles.getPropertyValue(style) + "; ";
                                }
                                newRules.push((selector.trim() + ' { ' + styleString.trim() + ' }'));
                            }
                        }
                        stylesheet.deleteRule(j);
                        for(k = 0; (rule = newRules[k]); k++) {
                            stylesheet.insertRule(rule, j);
                        }
                    }
                }
            }
            for (j = 0; (rule = rules[j]); j++) {
                if(rule.type === rule.STYLE_RULE && rule.selectorText.indexOf(':') < 0) {
                    var styles = rule.style, style, k;
                    var elements = document.querySelectorAll(rule.selectorText);
                    if(elements && elements.length) {
                        for(k = 0; (style = styles[k]); k++) {
                            var value = styles.getPropertyValue(style);
                            if(value.indexOf('%') < 0) {
                                var elements = document.querySelectorAll(rule.selectorText), element, m;
                                var keep = false;
                                for(m = 0; (element = elements[m]); m++) {
                                    var computed = window.getComputedStyle(element).getPropertyValue(style);
                                    var match1 = value === computed;
                                    styles.removeProperty(style);
                                    var computed = window.getComputedStyle(element).getPropertyValue(style);
                                    var match2 = value === computed;
                                    styles.setProperty(style, value);
                                    if( match1 && !match2 ) {
                                        keep = true;
                                    }
                                }
                                if(!keep) {
                                    styles.removeProperty(style);
                                }
                            }
                        }
                        ruleText += rule.cssText + "\n";
                    }
                } else {
                    ruleText += rule.cssText + "\n";
                }
            }
        }
    }
}
document.body.innerHTML = '<pre>' + ruleText + '<pre>';

Будущие зрители: это доступно в github как optiCSS (читайте: приятное для глаз)

Ответ 1

Это выглядит великолепно, и у меня есть только небольшое предложение.

Измените свой код следующим образом:

for (j = 0; rule = rules[j]; j++) {
  var styles = rule.style, style, k;
  var elements = document.querySelectorAll(rule.selectorText);

  if(elements.length) {
    for(k = 0; style = styles[k]; k++) {
      ...
    }
    console.log(rule.cssText);
  }
}

Это предотвратит вывод правил, которые не имеют соответствующего HTML-кода.

В this Fiddle правило li выводится с вашим кодом, но оно не выводится с помощью выше модификации.

Следующая задача состоит в том, чтобы упростить эти font, border, padding, & hellip; стили.


На основе вашего Изменить # 25 у вас останется строка, которая выглядит так:
html { margin-right: 0px; margin-left: 0px; height: 100%; }
body { margin-right: 0px; margin-left: 0px; height: 100%; margin-top: 0px; }
body { margin-bottom: 5px; background-color: rgb(255, 0, 0); }
@media (max-width: 500px) { 
  body { background: blue; }
}

Вот как это сделать:

html {margin-right:0px;margin-left:0px;height:100%;}
body {margin-right:0px;margin-left:0px;height:100%;margin-top:0px;margin-bottom:5px;background-color:rgb(255 0 0);}
@media (max-width: 500px) { 
  body { background: blue; }
}

В процессе вы получите два объекта:

html {"margin-right":"0px;","margin-left":"0px;","height":"100%;"}
body {"margin-right":"0px;","margin-left":"0px;","height":"100%;","margin-top":"0px;","margin-bottom":"5px;","background-color":"rgb(255, 0, 0);"}

Во-первых, сохраняйте отдельный вывод мультимедийных запросов, так как вы не хотите, чтобы он был затронут:

var ruleText = "", mediaText = "";
...
        if (styles.length) {
            ruleText += rule.cssText + "\n";
        }
    } else {
        mediaText += rule.cssText + "\n";
    }

Затем поместите это после цикла:

var inp= ruleText.split('\n'),
    out= '',
    selectors= {};

inp.forEach(function(val) {
  if(val) {
    var selector= val.split(/ *{/)[0],
        styles= val.split(/{ */)[1].split('}')[0].split(/; */);

    selectors[selector]= selectors[selector] || {};
    styles.forEach(function(val) {
      if(val) {
        var st= val.split(/ *: */);
        selectors[selector][st[0]]= st[1]+';';
      }
    });
  }
});

for(var i in selectors) {
  out+= i+' '+JSON.stringify(selectors[i]).replace(/[,"]/g,'')+'\n';
}
document.body.innerHTML= '<pre>'+out+mediaText+'</pre>';

Для простоты приведенный выше код предполагает, что в CSS не содержится строкового содержимого, содержащего двойные кавычки, точки с запятой, запятые или фигурные скобки. Это может усложнить ситуацию.

Fiddle