【问题标题】:How can I detect overwritten css properties?如何检测覆盖的 CSS 属性?
【发布时间】:2012-06-10 15:32:32
【问题描述】:

我可以使用 document.stylesheets 获取元素的所有 css 属性,但其中一些属性是不活动的,因为这些属性被覆盖了。在 firebug 中(chrome 开发者工具也有这个特性),如果有一个被覆盖的 css 属性,你会看到类似这样的东西:

我能想到的唯一方法是比较元素的活动 css 属性(在 jQuery $(element).css(property) 中)和在 document.stylesheets 中定义的 css 属性,但这不是一种可靠的方法。有什么建议吗?

【问题讨论】:

  • 浏览器必须知道,因为它会计算选择器的特异性来确定最终结果。开发工具所做的就是跟踪这些计算并向您展示它们。要在 JS 中做同样的事情,你必须构建一个完整的 CSS 解析器......
  • @Kolink,谢谢。似乎没有任何扩展程序就无法跟踪浏览器的计算,对吧?
  • 我可能遗漏了一些东西,但我可以在 DevTools 中看到被覆盖的属性的交叉方式与它们在 firebug 中的方式相同 (imgur.com/W9liZs8)。那么,这里有什么问题呢?
  • 你为什么要这样做?是否存在可以解决的潜在问题? (如果是这样,也许我们可以帮助解决该问题的其他方法。)
  • @andi 这对网站建设者和类似的应用程序很有用。

标签: javascript css firebug stylesheet google-chrome-devtools


【解决方案1】:

这个 sn-p 可能对你有用,它在过去帮助过我,将它应用到你的一个 CSS 文件中,你将能够通过查找元素 CSS 声明所在的特定位置,并通过跟踪其父级。我猜你在继承方面遇到了问题。

* { outline: 2px dotted red }
* * { outline: 2px dotted green }
* * * { outline: 2px dotted orange }
* * * * { outline: 2px dotted blue }
* * * * * { outline: 1px solid red }
* * * * * * { outline: 1px solid green }
* * * * * * * { outline: 1px solid orange }
* * * * * * * * { outline: 1px solid blue }

或者你可以添加一个 :hover 属性,它会让导航更容易一些,但是 sn-p 的基本原理是向你展示元素的嵌套,并且可以帮助你确定在哪里您的 CSS 中的问题是

【讨论】:

  • 实际上我正在尝试做一些类似firebug或chrome开发者工具的事情。不过谢谢。 :)
  • 我以为您已经在使用这些工具了,因为您包含了一张图片;)
  • @Xenology 是的,这张图片是为了解释他想要实现的目标,所以我们假设他想要重建 devTools,所以 OP 需要弄清楚如何检测覆盖的样式
  • 据我所知,没有办法检测过度使用的样式。了解您的风格何时何地可能发生冲突通常只是一个好习惯。我能看到的唯一其他选择是使用 JS 手动确定任何 CSS 选择器的权重,然后基于此构建一个对象。在与 CSS 或 JS 完全不同的领域中,这将是一个非常有趣的问题。检测过度使用的样式是浏览器作为 CSS 渲染级别所做的事情。因此,在 CSS 和 JS 中重新创建它会非常困难。
【解决方案2】:

您必须解析链接到页面的 CSS 文件才能确定正在使用的样式。您还必须确定可以使用哪些属性来定位 CSS 中的元素。以下是您必须遵循的流程:

元素可以通过以下方式成为目标:标签、ID、类、伪类和继承

然后你会有一个给定的元素,例如:

<a href="#" id="link" class="button">Woop</a>

默认情况下,该元素有几个与之关联的选择器。所有元素都可以继承父 CSS,因此您必须创建父元素的层次结构。以后的样式将优先。然后,您必须考虑标签a 以及任何也共享此选择器的父级,例如body a。同样,您必须创建继承属性的层次结构。您将对类、属性和伪类重复此过程。一旦你为元素编译了你的树列表,你就可以解析 CSS 文档以获得适当的样式。

这绝对是一项艰巨的任务,重新发明轮子毫无意义。我的建议是查看已经这样做的开源项目。 WebKit 是一个开源项目,已经构建了这个系统。考虑使用他们的实现作为起点,然后从那里进行更改。

【讨论】:

  • 这不是一个实用的解决方案,因为您无法解析从不同来源引用的 CSS。
  • 如果你使用easyXDM之类的东西,你可以。
  • 如果有 CORS,你一开始就不需要 easyXDM。如果没有 CORS,那么您不能在不使用代理服务器的情况下访问 CSS(然后无法访问整个同源点策略的内部网和 cookie)。 easyXDM 只是将不同的 API 捆绑在一起,它并没有为您提供物理上不可能的东西。
  • 你是对的,即使萤火虫也不支持这一点。我想知道您是否可以使用带有 JSONP 回调的托管代理?
  • 您可以使用yahoo YQL,但它并不完美(如果 css 是基于 cookie 或托管在跨 Intranet 上,则它不起作用)
【解决方案3】:

在 webkit 中,你可以使用getMatchedCSSRules 来实现你想要的。它按照浏览器应用的继承顺序返回一个 CSS 规则集,它是前一段时间的is what the webkit inspector used。对于像 Firefox 这样的基于 Gecko 的浏览器,似乎有一个可用的 polyfill here 虽然我还没有测试过。

一个基本的、有效的解决方案

The following code is also available as a fiddle

因为getMatchedCSSRulesonly works with inline stylesheets,您首先必须内联链接的样式表:

function inlineStyles() {
    var stylesheets = document.getElementsByTagName('link'),
        head = document.getElementsByTagName('head')[0];

    for (var i = 0; i < stylesheets.length; i++) {
        if (stylesheets[i].getAttribute('rel') == 'stylesheet') {
            (function (stylesheet) {
                var xmlhttp = new XMLHttpRequest();
                xmlhttp.open("GET", stylesheet.getAttribute('href'), true);
                xmlhttp.onreadystatechange = function () {
                    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                        var inlineStyle = document.createElement('style');
                        inlineStyle.setAttribute('type', 'text/css');
                        inlineStyle.innerText = xmlhttp.responseText;
                        head.replaceChild(inlineStyle, stylesheet);
                    }
                };
                xmlhttp.send();
            })(stylesheets[i]);
        } else {
            continue;
        }
    };
}

然后,大块:这是第一枪,随意改进。它可以处理inherit 规则和最多一个!important 定义,仅此而已。对于非常复杂的设置,这必须改进:

function getStyle(s, id) {
    var element = typeof id === 'string' ? document.getElementById(id) : id,
        css = window.getMatchedCSSRules(element),
        style = window.getComputedStyle(element),
        value = style[s],
        styles = [],
        rules = [],
        inherited, currentRule;

    // if there's a computed style, start calculation
    if (value) {

        // add matched rules if there are any
        if (css) {
            for (var i = 0; i < css.length; i++) {
                styles.push(css[i]);
            }
        }

        // add the element style attribute as a matched rule
        styles.push({
            style: element.style,
            cssText: 'element.style {' + element.getAttribute('style') + ' }'
        });

        for (var i = styles.length - 1; i >= 0; i--) {
            var def = styles[i],
                rule = {
                    index: rules.length,
                    style: s,
                    value: styles[i].style[s],
                    cssText: def.cssText
                };

            if (rule.value == 'inherit' && !currentRule) {
                if (inherited = getInherited(element, s, value)) {
                    rule.inheritedFrom = inherited;
                    currentRule = rule;
                    inherited = undefined;
                } else {
                    rules.push(rule);
                }
            } else if (rule.value == 'inherit' && currentRule && isImportant(s, value, def.cssText)) {
                if (inherited = getInherited(element, s, def)) {
                    rule.inheritedFrom = inherited;
                    rules.splice(currentRule.index, 0, currentRule);
                    currentRule = rule;
                    inherited = undefined;
                } else {
                    rules.push(rule);
                }
            } else if (rule.value == value && !currentRule) {
                currentRule = rule;
            } else if (rule.value == value && currentRule && isImportant(s, value, def.cssText)) {
                rules.splice(currentRule.index, 0, currentRule);
                currentRule = rule;
            } else if (rule.value.length) {
                rules.push(rule)
            }
        }

        return {
            current: currentRule,
            overwritten: rules
        };

    } else {
        return false;
    }
}

如果发生继承,我们沿着 DOM 节点查找使用此辅助函数定义 CSS 规则的元素并获取其样式:

function getInherited(element, s, value) {
    while (element.parentNode && window.getComputedStyle(element.parentNode)[s] == value) {
        element = element.parentNode;
    }

    if (element) {
        return getStyle(s, element).current;
    } else {
        return null;
    }
}

我们使用这个辅助函数来确定一个 CSS 规则是否被标记为重要:

function isImportant(s, style, text) {
    return new RegExp(s.replace(/([A-Z])/g, '-$1').toLowerCase() + ':\\s+' + style + '\\s+!important').test(text)
}

Have a look at the fiddle to see it working

【讨论】:

  • 演示中的输入有问题。它说请使用POST 请求。你能告诉我在输入中输入什么,以便我可以看到它的工作原理。
  • @Connor 使用 result 作为元素 ID 和 font-size 作为 CSS 属性,这些是我在小提琴的 CSS 和 HTML 中定义的,如果你想要任何其他的,你可以在直接拉小提琴并查询它们。
  • 我得到Uncaught TypeError: Cannot read property 'fontSize' of null 你用的是什么浏览器?
  • 任何 webkit 浏览器都可以:Chrome、Chromium 或 Safari。见第一句话,有一个基于 Gecko 的浏览器的 polyfill,但在这个解决方案中没有使用。
  • 是的,我正在使用 chrome。但是getMatchedCSSRules 是我一直在寻找的 :)
猜你喜欢
  • 2013-12-16
  • 1970-01-01
  • 2022-01-06
  • 2015-02-03
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
相关资源
最近更新 更多