Javascript 查找应用于元素的所有CSS规则

Javascript 查找应用于元素的所有CSS规则,javascript,css,Javascript,Css,许多工具/API提供了选择特定类或ID的元素的方法。还可以检查浏览器加载的原始样式表 但是,对于要呈现元素的浏览器,他们将编译所有CSS规则(可能来自不同的样式表文件)并将其应用于元素。这是您在Firebug或WebKit Inspector(元素的完整CSS继承树)中看到的 如何在不需要额外的浏览器插件的情况下在纯JavaScript中重现此功能 也许一个例子可以为我寻找的东西提供一些澄清: <style type="text/css"> p { color :red; }

许多工具/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>

p{颜色:红色;}
#说明{字体大小:20px;}

Lorem ipsum

这里p#description元素应用了两个CSS规则:红色和20px的字体大小


我想找到这些计算出的CSS规则的来源(颜色来自p规则等等)。

编辑:这个答案现在已被弃用,并被删除。离开历史背景。事实上,bug报告链接回这个问题,寻找使用这个问题的替代解决方案


似乎在又一个小时的研究之后,我终于回答了自己的问题

就这么简单:

window.getMatchedCSSRules(document.getElementById("description"))

(适用于WebKit/Chrome,可能还有其他版本)

看看这个库,它满足了用户的要求:


它适用于所有现代浏览器,可以追溯到IE6,可以为您提供规则和属性集合,比如Firebug(事实上它比Firebug更精确),还可以计算任何规则的相对或绝对特异性。唯一需要注意的是,尽管它了解静态媒体类型,但它不了解媒体查询。

由于这个问题目前没有一个轻量级(非库)跨浏览器兼容的答案,我将尝试提供一个:

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:

调用
css(document.getElementById('elementId'))
将返回一个数组,其中每个css规则对应一个与传递的元素匹配的元素。
如果您想了解有关每个规则的更多具体信息,请查看文档。

以下是s.B.答案的一个版本,它还将在匹配媒体查询中返回匹配规则。我已经删除了
*.rules | |*.cssRules
合并和
.matches
实现查找器;添加一个多边形填充,或者在需要时将这些线条添加回

此版本还返回
CSSStyleRule
对象,而不是规则文本。我认为这更有用一点,因为通过这种方式可以更容易地以编程方式探索规则的细节

咖啡:

getMatchedCSSRules=(元素)->
sheets=document.styleSheets
匹配=[]
loopRules=(规则)->
规则中的规则
CSSMediaRule的if规则实例
如果window.matchMedia(rule.conditionText).matches
loopRules rule.cssRules
else if CSSStyleRule的规则实例
如果element.matches rule.selectorText
匹配推送规则
返回
loopRules sheet.cssRules用于图纸中的图纸
返回匹配
JS:

函数getMatchedCSSRules(元素){
变量i,len,matching=[],sheets=document.styleSheets;
函数循环规则(规则){
变量i,len,规则;
for(i=0,len=rules.length;i
2017年4月12日短版 挑战者号出现了

var getMatchedCSSRules=(el,css=el.ownerDocument.styleSheets)=>
[].concat(…[…css].map(s=>[…s.cssRules | |[]])/*1*/
.filter(r=>el.matches(r.selectorText));/*2 */
/*1*/
构建所有规则的平面数组。
/*2*/
丢弃不匹配的规则

基于同一页上的@S.B

例1 例2
var getMatchedCSSRules=(el,css=el.ownerDocument.styleSheets)=>
[].concat(…[…css].map(s=>[…s.cssRules | |[]))
.filter(r=>el.matches(r.selectorText));
功能开始(大,显示){
var r=getMatchedCSSRules(大);
打印信息:
var f=(dd,rr,ee=“\n”)=>dd+rr.cssText.slice(0,50)+ee;
show.value+=“--------------规则:--------------\n”;
show.value+=f(“规则1:,r[0]);
show.value+=f(“规则2:,r[1]);
show.value+=f(“内联:”,big.style);
show.value+=f(“计算:”,getComputedStyle(大),“(…)\n”);
show.value+=“-----------样式元素(HTML):----------\n”;
show.value+=r[0].parentStyleSheet.ownerNode.outerHTML;
}
Go(…document.queryselectoral(“#big,#show”)
.red{color:red;}
#大{font size:20px;}
Lorem ipsum
var GetMatchedCSSRules=(elem,css=document.styleSheets)=>Array.from(css)
.map(s=>Array.from(s.cssRules).filter(r=>elem.matches(r.selectorText)))
.减少((a,b)=>a.concat(b));
功能Go(段落,打印){
var规则=GetMatchedCSSRules(第段);
打印信息:
print.value+=“规则1:+规则[0].cssText+“\n”;
print.value+=“规则2:”+规则[1].cssText+“\n\n”;
print.value+=规则[0]。parentStyleSheet.ownerNode.outerHTML;
}
Go(document.getElementById(“说明”)、document.getElementById(“打印”)
p{color:red;}
#说明{字体大小:20px;}

Lorem ipsum


为了确保IE9+,我编写了一个函数,用于计算请求元素及其子元素的CSS,并在下面的代码段中提供了将其保存到新类名的可能性(如果需要)

/**
  * @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;
}

这是我的
getMatchedCSSRules/**
  * @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);');
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
}
<html><head>
<title>CSS Test</title>
<style id="style-a">
li {color: #333; font-size: 20px !important;}
li.bb {color: #600; font-size: 10px;}
p {margin: 5px;}
p {margin-bottom: 10px;}
</style>
<script>
window.addEventListener('DOMContentLoaded', async () => {
    const selector = 'li';
    // const selector = 'li.bb';
    const exempleValues = {
        'color': ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'],
        'font-size': ['10px', '12px'],
    };
    const delay = (t) => new Promise((k, e) => {setTimeout(k, t)});

    for(const element of document.querySelectorAll(selector)) {
        const elementCss = document.defaultView.getComputedStyle(element);
        for(const sheet of document.styleSheets) {
            for(const rule of sheet.cssRules) {
                if(rule.selectorText !== selector) {
                    continue;
                }
                for(const properyName of rule.style) {
                    const currentValue = rule.style[properyName];
                    const priority = rule.style.getPropertyPriority(properyName)
                    if(!exempleValues[properyName]) {
                        console.warn('no exemple values for', properyName);
                        continue;
                    }
                    const exempleValue = exempleValues[properyName][exempleValues[properyName][0] === currentValue ? 1 : 0];
                    rule.style.setProperty(properyName, exempleValue, priority);
                    await delay(100);
                    if(exempleValue === elementCss[properyName]) {
                        console.log(selector, properyName, currentValue, priority || false, true, 'in use', element, sheet.ownerNode);
                    } else {
                        console.log(selector, properyName, currentValue, priority || false, false, 'overrided', element);
                    }
                    rule.style.setProperty(properyName, currentValue, priority);
                    await delay(100);
                }
            }
        }
    }
}, {once: true});
</script>
</head><body>
<h1>CSS Test</h1>
<p>html-file for testing css</p>
<ul>
    <li>AAAA</li>
    <li class="bb">BBBB</li>
    <li>CCCC</li>
</ul>
</body></html>