Ответ 1
Кажется, мне удалось ответить на мой вопрос после еще одного часа исследования.
Это так просто:
window.getMatchedCSSRules(document.getElementById("description"))
(Работает в WebKit/Chrome, возможно, и с другими)
Многие инструменты /API предоставляют способы выбора элементов определенных классов или идентификаторов. Также можно проверить загруженные браузером исходные таблицы стилей.
Однако, чтобы браузеры отображали элемент, они собирают все правила CSS (возможно, из разных файлов стилей) и применяют его к элементу. Это то, что вы видите с Firebug или WebKit Inspector - полное дерево наследования CSS для элемента.
Как я могу воспроизвести эту функцию в чистом JavaScript, не требуя дополнительных плагинов для браузера?
Возможно, пример может дать некоторые пояснения к тому, что я ищу:
<style type="text/css">
p { color :red; }
#description { font-size: 20px; }
</style>
<p id="description">Lorem ipsum</p>
Здесь элемент описания p # имеет два правила CSS: красный цвет и размер шрифта 20 px.
Я хотел бы найти источник, откуда берутся эти вычисленные правила CSS (цвет входит в правило p и т.д.).
Кажется, мне удалось ответить на мой вопрос после еще одного часа исследования.
Это так просто:
window.getMatchedCSSRules(document.getElementById("description"))
(Работает в WebKit/Chrome, возможно, и с другими)
Поскольку этот вопрос в настоящее время не имеет легкого (небиблиотечного), совместимого с кросс-браузером ответа, я постараюсь предоставить один:
function css(el) {
var sheets = document.styleSheets, ret = [];
el.matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector
|| el.msMatchesSelector || el.oMatchesSelector;
for (var i in sheets) {
var rules = sheets[i].rules || sheets[i].cssRules;
for (var r in rules) {
if (el.matches(rules[r].selectorText)) {
ret.push(rules[r].cssText);
}
}
}
return ret;
}
JSFiddle: http://jsfiddle.net/HP326/6/
Вызов css(document.getElementById('elementId'))
возвращает массив с элементом для каждого правила CSS, который соответствует переданному элементу.
Если вы хотите узнать более конкретную информацию о каждом правиле, ознакомьтесь с документацией CSSRule.
Посмотрите на эту библиотеку, которая делает то, что было предложено: http://www.brothercake.com/site/resources/scripts/cssutilities/
Он работает во всех современных браузерах прямо в IE6, может предоставить вам коллекции правил и свойств, такие как Firebug (на самом деле он более точный, чем Firebug), а также может рассчитать относительную или абсолютную специфичность любого правила. Единственное предостережение в том, что, хотя он понимает статические типы носителей, он не понимает медиа-запросы.
Появится Challenger.
var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) =>
[].concat(...[...css].map(s => [...s.cssRules||[]])) /* 1 */
.filter(r => el.matches(r.selectorText)); /* 2 */
Линия /* 1 */
строит плоский массив всех правил.
Строка /* 2 */
отменяет правила несоответствия.
На основе function css(el)
by @S.B. на той же странице.
var div = iframedoc.querySelector("#myelement");
var rules = getMatchedCSSRules(div, iframedoc.styleSheets);
console.log(rules[0].parentStyleSheet.ownerNode, rules[0].cssText);
var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) =>
[].concat(...[...css].map(s => [...s.cssRules||[]]))
.filter(r => el.matches(r.selectorText));
function Go(big,show) {
var r = getMatchedCSSRules(big);
PrintInfo:
var f = (dd,rr,ee="\n") => dd + rr.cssText.slice(0,50) + ee;
show.value += "--------------- Rules: ----------------\n";
show.value += f("Rule 1: ", r[0]);
show.value += f("Rule 2: ", r[1]);
show.value += f("Inline: ", big.style);
show.value += f("Computed: ", getComputedStyle(big), "(…)\n");
show.value += "-------- Style element (HTML): --------\n";
show.value += r[0].parentStyleSheet.ownerNode.outerHTML;
}
Go(...document.querySelectorAll("#big,#show"));
.red {color: red;}
#big {font-size: 20px;}
<h3 id="big" class="red" style="margin: 0">Lorem ipsum</h3>
<textarea id="show" cols="70" rows="10"></textarea>
Здесь версия S.B. ответ, который также возвращает соответствующие правила в совпадающих медиа-запросах. Я удалил слияние *.rules || *.cssRules
и поиск .matches
; добавьте polyfill или добавьте эти строки, если они вам понадобятся.
Эта версия также возвращает объекты CSSStyleRule
, а не текст правила. Я думаю, что это немного более полезно, так как специфика правил может быть легче диагностирована программно таким образом.
Кофе
getMatchedCSSRules = (element) ->
sheets = document.styleSheets
matching = []
loopRules = (rules) ->
for rule in rules
if rule instanceof CSSMediaRule
if window.matchMedia(rule.conditionText).matches
loopRules rule.cssRules
else if rule instanceof CSSStyleRule
if element.matches rule.selectorText
matching.push rule
return
loopRules sheet.cssRules for sheet in sheets
return matching
JS:
function getMatchedCSSRules(element) {
var i, len, matching = [], sheets = document.styleSheets;
function loopRules(rules) {
var i, len, rule;
for (i = 0, len = rules.length; i < len; i++) {
rule = rules[i];
if (rule instanceof CSSMediaRule) {
if (window.matchMedia(rule.conditionText).matches) {
loopRules(rule.cssRules);
}
} else if (rule instanceof CSSStyleRule) {
if (element.matches(rule.selectorText)) {
matching.push(rule);
}
}
}
};
for (i = 0, len = sheets.length; i < len; i++) {
loopRules(sheets[i].cssRules);
}
return matching;
}
var GetMatchedCSSRules = (elem, css = document.styleSheets) => Array.from(css)
.map(s => Array.from(s.cssRules).filter(r => elem.matches(r.selectorText)))
.reduce((a,b) => a.concat(b));
function Go(paragraph, print) {
var rules = GetMatchedCSSRules(paragraph);
PrintInfo:
print.value += "Rule 1: " + rules[0].cssText + "\n";
print.value += "Rule 2: " + rules[1].cssText + "\n\n";
print.value += rules[0].parentStyleSheet.ownerNode.outerHTML;
}
Go(document.getElementById("description"), document.getElementById("print"));
p {color: red;}
#description {font-size: 20px;}
<p id="description">Lorem ipsum</p>
<textarea id="print" cols="50" rows="12"></textarea>
Обеспечение IE9 +, я написал функцию, которая вычисляет CSS для запрошенного элемента и его дочерних элементов и дает возможность сохранить его в новом классеName, если это необходимо в фрагменте ниже.
/**
* @function getElementStyles
*
* Computes all CSS for requested HTMLElement and its child nodes and applies to dummy class
*
* @param {HTMLElement} element
* @param {string} className (optional)
* @param {string} extras (optional)
* @return {string} CSS Styles
*/
function getElementStyles(element, className, addOnCSS) {
if (element.nodeType !== 1) {
return;
}
var styles = '';
var children = element.getElementsByTagName('*');
className = className || '.' + element.className.replace(/^| /g, '.');
addOnCSS = addOnCSS || '';
styles += className + '{' + (window.getComputedStyle(element, null).cssText + addOnCSS) + '}';
for (var j = 0; j < children.length; j++) {
if (children[j].className) {
var childClassName = '.' + children[j].className.replace(/^| /g, '.');
styles += ' ' + className + '>' + childClassName +
'{' + window.getComputedStyle(children[j], null).cssText + '}';
}
}
return styles;
}
Использование
getElementStyles(document.getElementByClassName('.my-class'), '.dummy-class', 'width:100%;opaity:0.5;transform:scale(1.5);');