许多工具/ API提供了选择特定类或ID元素的方法.还可以检查浏览器加载的原始样式表.
但是,对于浏览器呈现元素,它们将编译所有CSS规则(可能来自不同的样式表文件)并将其应用于元素.这就是您在Firebug或WebKit Inspector中看到的内容 - 一个元素的完整CSS继承树.
如何在纯JavaScript中重现此功能而无需其他浏览器插件?
也许一个例子可以为我正在寻找的东西提供一些澄清:
<style type="text/css">
p { color :red; }
#description { font-size: 20px; }
</style>
<p id="description">Lorem ipsum</p>
Run Code Online (Sandbox Code Playgroud)
这里p#描述元素应用了两个CSS规则:红色和20像素的字体大小.
我想找到这些计算出的CSS规则源自的来源(颜色来自p规则等).
S.B*_*.B. 66
由于这个问题目前没有轻量级(非库),跨浏览器兼容的答案,我将尝试提供一个:
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;
}
Run Code Online (Sandbox Code Playgroud)
JSFiddle:http://jsfiddle.net/HP326/6/
调用css(document.getElementById('elementId'))将返回一个数组,其中包含与传递的元素匹配的每个CSS规则的元素.如果要查找有关每个规则的更多特定信息,请查看CSSRule对象文档.
cgb*_*rom 22
编辑:此答案现已弃用,不再适用于Chrome 64+.留下历史背景.事实上,错误报告链接回到这个问题,以寻找使用它的替代解决方案.
似乎我在经过一个小时的研究后设法回答了我自己的问题.
这很简单:
window.getMatchedCSSRules(document.getElementById("description"))
Run Code Online (Sandbox Code Playgroud)
(适用于WebKit/Chrome,也可能适用于其他人)
小智 17
看看这个库,它可以满足要求:http: //www.brothercake.com/site/resources/scripts/cssutilities/
它可以在所有现代浏览器中使用,直到IE6,可以为你提供像Firebug这样的规则和属性集合(事实上它比Firebug更准确),并且还可以计算任何规则的相对或绝对特异性.唯一需要注意的是,虽然它理解静态媒体类型,但它不了解媒体查询.
7vu*_*0hy 15
挑战者出现了.
var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) =>
[].concat(...[...css].map(s => [...s.cssRules||[]])) /* 1 */
.filter(r => el.matches(r.selectorText)); /* 2 */
Run Code Online (Sandbox Code Playgroud)
Line /* 1 */构建所有规则的平面数组.
线/* 2 */丢弃不匹配的规则.
基于@SB在同一页面上的功能css(el).
var div = iframedoc.querySelector("#myelement");
var rules = getMatchedCSSRules(div, iframedoc.styleSheets);
console.log(rules[0].parentStyleSheet.ownerNode, rules[0].cssText);
Run Code Online (Sandbox Code Playgroud)
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"));Run Code Online (Sandbox Code Playgroud)
.red {color: red;}
#big {font-size: 20px;}Run Code Online (Sandbox Code Playgroud)
<h3 id="big" class="red" style="margin: 0">Lorem ipsum</h3>
<textarea id="show" cols="70" rows="10"></textarea>Run Code Online (Sandbox Code Playgroud)
@import,@media.也许有一天我会解决这些缺点.
这是一个更全面的实现,取自某人的GitHub页面 (从这个原始代码分叉,通过Bugzilla).写给Gecko和IE,但据传也与Blink合作.
2017年5月4日:特异性计算器已经出现了重要的错误,我现在已经修复了.(我无法通知作者,因为我没有GitHub帐户.)
2018年8月12日:最近的Chrome更新似乎已将对象范围(this)与分配给自变量的方法分离.因此调用matcher(selector)已停止工作.取而代之的matcher.call(el, selector)是解决了它.
// polyfill window.getMatchedCSSRules() in FireFox 6+
if (typeof window.getMatchedCSSRules !== 'function') {
var ELEMENT_RE = /[\w-]+/g,
ID_RE = /#[\w-]+/g,
CLASS_RE = /\.[\w-]+/g,
ATTR_RE = /\[[^\]]+\]/g,
// :not() pseudo-class does not add to specificity, but its content does as if it was outside it
PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g,
PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g;
// convert an array-like object to array
function toArray(list) {
return [].slice.call(list);
}
// handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same
function getSheetRules(stylesheet) {
var sheet_media = stylesheet.media && stylesheet.media.mediaText;
// if this sheet is disabled skip it
if ( stylesheet.disabled ) return [];
// if this sheet's media is specified and doesn't match the viewport then skip it
if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return [];
// get the style rules of this sheet
return toArray(stylesheet.cssRules);
}
function _find(string, re) {
var matches = string.match(re);
return matches ? matches.length : 0;
}
// calculates the specificity of a given `selector`
function calculateScore(selector) {
var score = [0,0,0],
parts = selector.split(' '),
part, match;
//TODO: clean the ':not' part since the last ELEMENT_RE will pick it up
while (part = parts.shift(), typeof part == 'string') {
// find all pseudo-elements
match = _find(part, PSEUDO_ELEMENTS_RE);
score[2] += match;
// and remove them
match && (part = part.replace(PSEUDO_ELEMENTS_RE, ''));
// find all pseudo-classes
match = _find(part, PSEUDO_CLASSES_RE);
score[1] += match;
// and remove them
match && (part = part.replace(PSEUDO_CLASSES_RE, ''));
// find all attributes
match = _find(part, ATTR_RE);
score[1] += match;
// and remove them
match && (part = part.replace(ATTR_RE, ''));
// find all IDs
match = _find(part, ID_RE);
score[0] += match;
// and remove them
match && (part = part.replace(ID_RE, ''));
// find all classes
match = _find(part, CLASS_RE);
score[1] += match;
// and remove them
match && (part = part.replace(CLASS_RE, ''));
// find all elements
score[2] += _find(part, ELEMENT_RE);
}
return parseInt(score.join(''), 10);
}
// returns the heights possible specificity score an element can get from a give rule's selectorText
function getSpecificityScore(element, selector_text) {
var selectors = selector_text.split(','),
selector, score, result = 0;
while (selector = selectors.shift()) {
if (matchesSelector(element, selector)) {
score = calculateScore(selector);
result = score > result ? score : result;
}
}
return result;
}
function sortBySpecificity(element, rules) {
// comparing function that sorts CSSStyleRules according to specificity of their `selectorText`
function compareSpecificity (a, b) {
return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText);
}
return rules.sort(compareSpecificity);
}
// Find correct matchesSelector impl
function matchesSelector(el, selector) {
var matcher = el.matchesSelector || el.mozMatchesSelector ||
el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector;
return matcher.call(el, selector);
}
//TODO: not supporting 2nd argument for selecting pseudo elements
//TODO: not supporting 3rd argument for checking author style sheets only
window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) {
var style_sheets, sheet, sheet_media,
rules, rule,
result = [];
// get stylesheets and convert to a regular Array
style_sheets = toArray(window.document.styleSheets);
// assuming the browser hands us stylesheets in order of appearance
// we iterate them from the beginning to follow proper cascade order
while (sheet = style_sheets.shift()) {
// get the style rules of this sheet
rules = getSheetRules(sheet);
// loop the rules in order of appearance
while (rule = rules.shift()) {
// if this is an @import rule
if (rule.styleSheet) {
// insert the imported stylesheet's rules at the beginning of this stylesheet's rules
rules = getSheetRules(rule.styleSheet).concat(rules);
// and skip this rule
continue;
}
// if there's no stylesheet attribute BUT there IS a media attribute it's a media rule
else if (rule.media) {
// insert the contained rules of this media rule to the beginning of this stylesheet's rules
rules = getSheetRules(rule).concat(rules);
// and skip it
continue
}
// check if this element matches this rule's selector
if (matchesSelector(element, rule.selectorText)) {
// push the rule to the results set
result.push(rule);
}
}
}
// sort according to specificity
return sortBySpecificity(element, result);
};
}
Run Code Online (Sandbox Code Playgroud)
= match → += matchreturn re ? re.length : 0; → return matches ? matches.length : 0;_matchesSelector(element, selector) → matchesSelector(element, selector)matcher(selector) → matcher.call(el, selector)这是我的getMatchedCSSRules支持@media查询的函数版本。
const getMatchedCSSRules = (el) => {
let rules = [...document.styleSheets]
rules = rules.filter(({ href }) => !href)
rules = rules.map((sheet) => [...(sheet.cssRules || sheet.rules || [])].map((rule) => {
if (rule instanceof CSSStyleRule) {
return [rule]
} else if (rule instanceof CSSMediaRule && window.matchMedia(rule.conditionText)) {
return [...rule.cssRules]
}
return []
}))
rules = rules.reduce((acc, rules) => acc.concat(...rules), [])
rules = rules.filter((rule) => el.matches(rule.selectorText))
rules = rules.map(({ style }) => style)
return rules
}
Run Code Online (Sandbox Code Playgroud)