如何在JavaScript中检索已应用于元素的样式,排除默认用户代理样式(仅限内联+样式表样式)。
基本上,您可以在自己喜欢的开发者工具的Computed选项卡中看到所有用户样式:
没有框架,IE8 +,Edge,Chrome和Firefox。
我希望答案是getComputedStyle减去getDefaultComputedStyle的结果,但是以跨浏览器的方式。看到所有开发人员工具都能够做到这一点,必须有一个解决方案:)
答案 0 :(得分:6)
有一个名为' styleSheets'的文档的只读属性。
var styleSheetList = document.styleSheets;
https://developer.mozilla.org/en-US/docs/Web/API/Document/styleSheets
通过使用此功能,您可以访问作者应用的所有样式。
有一个类似的问题,但不是重复,在这里:
Is it possible to check if certain CSS properties are defined inside the style tag with Javascript?
您可以使用我刚才提到的问题的accepted answer来从元素中获取应用的样式,不包括默认的用户代理样式。
该答案并未提供该元素自己的style
属性内容,因此我对代码进行了一些改进:
var proto = Element.prototype;
var slice = Function.call.bind(Array.prototype.slice);
var matches = Function.call.bind(proto.matchesSelector ||
proto.mozMatchesSelector || proto.webkitMatchesSelector ||
proto.msMatchesSelector || proto.oMatchesSelector);
// Returns true if a DOM Element matches a cssRule
var elementMatchCSSRule = function(element, cssRule) {
return matches(element, cssRule.selectorText);
};
// Returns true if a property is defined in a cssRule
var propertyInCSSRule = function(prop, cssRule) {
return prop in cssRule.style && cssRule.style[prop] !== "";
};
// Here we get the cssRules across all the stylesheets in one array
var cssRules = slice(document.styleSheets).reduce(function(rules, styleSheet) {
return rules.concat(slice(styleSheet.cssRules));
}, []);
var getAppliedCss = function(elm) {
// get only the css rules that matches that element
var elementRules = cssRules.filter(elementMatchCSSRule.bind(null, elm));
var rules =[];
if(elementRules.length) {
for(i = 0; i < elementRules.length; i++) {
var e = elementRules[i];
rules.push({
order:i,
text:e.cssText
})
}
}
if(elm.getAttribute('style')) {
rules.push({
order:elementRules.length,
text:elm.getAttribute('style')
})
}
return rules;
}
function showStyle(){
var styleSheetList = document.styleSheets;
// get a reference to an element, then...
var div1 = document.getElementById("div1");
var rules = getAppliedCss(div1);
var str = '';
for(i = 0; i < rules.length; i++) {
var r = rules[i];
str += '<br/>Style Order: ' + r.order + ' | Style Text: ' + r.text;
}
document.getElementById("p1").innerHTML = str;
}
&#13;
#div1 {
float:left;
width:100px;
}
div {
text-align:center;
}
&#13;
<div id="div1" style="font-size:14px;">
Lorem ipsum
</div>
<br/>
<br/>
<a href="javascript:;" onclick="showStyle()"> Show me the style. </a>
<p id="p1"><p>
&#13;
答案 1 :(得分:3)
所有开发人员工具都可以作弊,因为他们可以访问构建它们的浏览器的默认规则。
我认为following approach可能有用。
div
或p
)。iframe
中
例如,如果您确定没有针对任何p
元素的规则,那么附加到正文可能会更有效。 它似乎在实践中运作得相当好。我只在Firefox和Chrome中对此进行了测试,但我认为它也可以在其他浏览器中使用 - 除了我使用for...in和for...of的事实,但可以轻松地重写它。请注意,不会仅报告您指定的属性,还会报告受您指定的属性影响的某些属性。例如,边框颜色与文本颜色by design匹配,因此即使您只设置了color: white
,也会报告为不同。
总结一下,我已经在你的一条评论中发布了一个例子,并在其中添加了一个getNonDefaultStyles
函数,我认为它可以满足您的需求。它当然可以被修改为缓存say div
元素的默认样式,因此在重复调用中更有效(因为修改DOM is expensive),但它显示了要点。
下面的代码段显示了如何实现将元素附加到正文的版本。由于StackOverflow的限制,无法在代码段中显示iframe
版本。这可以在JSFiddle。以下代码段也可以在Fiddle中找到。
var textarea = document.getElementById("textarea"),
paragraph = document.getElementById("paragraph");
/**
* Computes applied styles, assuming no rules targeting a specific element.
*/
function getNonDefaultStyles(el) {
var styles = {},
computed = window.getComputedStyle(el),
notTargetedContainer = document.createElement('div'),
elVanilla = document.createElement(el.tagName);
document.body.appendChild(notTargetedContainer);
notTargetedContainer.appendChild(elVanilla);
var vanilla = window.getComputedStyle(elVanilla);
for (let key of computed) {
if (vanilla[key] !== computed[key]) {
styles[key] = computed[key];
}
}
document.body.removeChild(notTargetedContainer);
return styles;
}
var paragraphStyles = getNonDefaultStyles(paragraph);
for (let style in paragraphStyles) {
textarea.value += style + ": " + paragraphStyles[style] + "\n";
}
&#13;
#paragraph {
background: red;
}
textarea {
width: 300px;
height: 400px;
}
&#13;
<p id="paragraph" style="color: white">
I am a DIV
</p>
<p>
User styles:
</p>
<textarea id="textarea"></textarea>
&#13;
答案 2 :(得分:1)
这是一个函数,该函数从页面上的内联样式(HTML style
属性)或样式表中获取已应用于元素的所有CSS规则。它还捕获了CSS动画以及:active
,:hover
,::before
和::after
选择器的相关关键帧。
function getAppliedCssData(el) {
// we create a unique id so we can generate unique ids for renaming animations
let uniqueId = "id" + Math.random().toString().slice(2) + Math.random().toString().slice(2);
let allRules = [...document.styleSheets].map(s => {
let rules = [];
try { rules.push(...s.cssRules) } catch(e) {} // we ignore cross-domain stylesheets with restrictive CORs headers
return rules;
}).flat();
let styleRules = allRules.filter(rule => rule.type === CSSRule.STYLE_RULE)
let fontFaceRules = allRules.filter(rule => rule.type === CSSRule.FONT_FACE_RULE);
let keyframesRules = allRules.filter(rule => rule.type === CSSRule.KEYFRAMES_RULE);
let matchingDefaultRules = styleRules.filter(rule => el.matches(rule.selectorText));
let nonMatchingRules = styleRules.filter(rule => !el.matches(rule.selectorText));
let matchingHoverRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/([^(])(:hover)\b/g, "$1")));
let matchingActiveRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/([^(])(:active)\b/g, "$1")));
let matchingBeforeRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/::before\b/g, "")));
let matchingAfterRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/::after\b/g, "")));
let allMatchingStyleRules = [...matchingActiveRules, ...matchingDefaultRules, ...matchingHoverRules, ...matchingBeforeRules, ...matchingAfterRules];
let matchingAnimationNames = allMatchingStyleRules.map(rule => rule.style.animationName).filter(n => n.trim());
let matchingKeyframeRules = keyframesRules.filter(rule => matchingAnimationNames.includes(rule.name));
// make name changes before actually grabbing the style text of each type
allMatchingStyleRules.forEach(rule => rule.style.animationName = rule.style.animationName+uniqueId);
matchingKeyframeRules.forEach(rule => rule.name = rule.name+uniqueId);
let matchingDefaultStyles = matchingDefaultRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ") + (el.getAttribute('style') || ""); // important to add these last because inline styles are meant to override stylesheet styles (unless !important is used)
let matchingHoverStyles = matchingHoverRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingActiveStyles = matchingActiveRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingBeforeStyles = matchingBeforeRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingAfterStyles = matchingAfterRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingKeyframeStyles = matchingKeyframeRules.map(rule => rule.cssText).join(" ");
// undo the rule name changes because this actually affects the whole document:
matchingKeyframeRules.forEach(rule => rule.name = rule.name.replace(uniqueId, ""));
allMatchingStyleRules.forEach(rule => rule.style.animationName = rule.style.animationName.replace(uniqueId, ""));
let data = {
uniqueId,
defaultStyles: matchingDefaultStyles,
hoverStyles: matchingHoverStyles,
activeStyles: matchingActiveStyles,
keyframeStyles: matchingKeyframeStyles,
beforeStyles: matchingBeforeStyles,
afterStyles: matchingAfterStyles,
}
return data;
}
:focus
,:focus-within
和:visited
选择器不包括在内,但可以轻松添加。
答案 3 :(得分:0)
我过去曾使用过这个功能......
function get_style(obj,nam) { //obj = HTML element, nam = style property
var val = "";
if(document.defaultView && document.defaultView.getComputedStyle) {
nam = nam.replace(/[A-Z]/g,function(str) { //convert name into hypenated
return "-"+str.toLowerCase();
});
val = document.defaultView.getComputedStyle(obj,"").getPropertyValue(nam); //get current style
}
else if(obj.currentStyle) {
nam = nam.replace(/\-(\w)/g,function(str,p1) { //convert name into camel case
return p1.toUpperCase();
});
val = obj.currentStyle[nam]; //get current style
}
return val;
}
&#13;
它允许您将样式属性传递为hypenated(background-color
)或camel case(backgroundColor
)并根据其使用的方法替换它。
这也涵盖旧浏览器,甚至是老IE!