值得一看
广告
彩虹云商城
广告

热门广告位

获取元素期望样式的教程

 获取元素期望样式的教程

本文旨在提供一种获取元素期望 CSS 属性的方法,即使这些样式是通过 JavaScript 动态设置的。传统的 `getComputedStyle` 方法返回的是元素最终应用的样式,而本文介绍的方法则能够提取开发者在样式表或内联样式中定义的原始样式,并考虑到 CSS 规则的优先级,帮助开发者更准确地了解元素的设计意图。
在 Web 开发中,我们经常需要获取元素的样式信息。`window.getComputedStyle` 方法可以获取元素最终应用的样式,但有时我们更需要获取开发者在 CSS 样式表中定义的原始样式,特别是当样式是通过 JavaScript 动态修改时。本教程将介绍一种通过遍历样式表并结合 CSS 选择器优先级来获取元素期望样式的方法。
### 原理
该方法的核心思想是遍历文档中的所有样式表,找到适用于目标元素的 CSS 规则,并提取这些规则中定义的样式属性。由于 CSS 规则存在优先级(specificity),我们需要比较不同规则的优先级,选择优先级最高的规则中定义的样式作为元素的期望样式。
### 实现步骤
1. **准备样式表:** 解决跨域问题。由于跨域限制,我们可能无法直接访问外部样式表的 `cssRules` 属性。为了解决这个问题,我们需要将外部样式表的内容读取并插入到文档的 `<head>` 中。
“`javascript
function prepareStylesheet(sheet) {
try {
sheet.cssRules;
} catch (e) {
if (e.name === ‘SecurityError’) {
let nextElement = sheet.ownerNode.nextSibling;
let parentNode = sheet.ownerNode.parentNode;
sheet.ownerNode.parentNode.removeChild(sheet.ownerNode);
fetch(sheet.href).then(resp => resp.text()).then(css => {
let style = document.createElement(‘style’);
style.innerText = css;
if(nextElement === null){
parentNode.appendChild(style);
}
else{
parentNode.insertBefore(style, nextElement);
}
});
}
}
}
for(let i = 0; i < document.styleSheets.length; i++){
prepareStylesheet(document.styleSheets[i]);
}

  1. 获取元素样式: 遍历样式表,找到适用于目标元素的规则,并提取样式属性。

    function getStylesFromStylesheets(elementToInvestigate) {
    let givenStyles = {};
    for (let i = 0; i < document.styleSheets.length; i++) {
    let rules = document.styleSheets[i].cssRules;
    for (let j = 0; j < rules.length; j++) {
    let rule = rules[j];
    if (typeof(rule.selectorText) === "undefined") {
    continue;
    }
    let split = rule.selectorText.split(",");
    for(let l = 0; l < split.length; l++){
    let selector = split[l];
    let elements = document.querySelectorAll(selector);
    let applies = false;
    for (let k = 0; k < elements.length; k++) {
    if (elements[k] === elementToInvestigate) {
    applies = true;
    break;
    }
    }
    if (applies === true) {
    let styles = rule.style;
    for (let k = 0; k < styles.length; k++) {
    let styleName = styles[k];
    let styleValue = styles[styleName];
    let newSpecificity = calculateSingle( selector);
    if (typeof(givenStyles[styleName]) !== "undefined") {
    let earlierSelector = givenStyles[styleName].selector;
    let earlierSpecificity = givenStyles[styleName].specificity;
    let newHasMoreSpecificity = compareSpecifities( newSpecificity, earlierSpecificity);
    if (newHasMoreSpecificity === true) {
    givenStyles[styleName] = {
    style: styleValue,
    specificity: newSpecificity
    }
    }
    } else {
    givenStyles[styleName] = {
    style: styleValue,
    specificity: newSpecificity
    }
    }
    }
    }
    }
    }
    if (elementToInvestigate.style.length > 0) {
    for (let j = 0; j < elementToInvestigate.style.length; j++) {
    let styleName = elementToInvestigate.style[j];
    let styleValue = elementToInvestigate.style[styleName];
    givenStyles[styleName] = {
    style: styleValue,
    specificity: [1, 1, 1, 1] //not needed here
    }
    }
    }
    }
    return givenStyles;
    }
  2. 计算 CSS 选择器优先级: 使用 calculateSingle 函数计算 CSS 选择器的优先级。该函数基于 CSS 规范,将选择器优先级表示为一个四元组 [a, b, c, d],其中 a 代表 ID 选择器的数量,b 代表类选择器、属性选择器和伪类选择器的数量,c 代表元素选择器和伪元素选择器的数量。

    function calculateSingle(input) {
    var selector = input,
    findMatch,
    typeCount = {
    'a': 0,
    'b': 0,
    'c': 0
    },
    parts = [],
    attributeRegex = /([[^]]+])/g,
    idRegex = /(#[^#s+>~.[:)]+)/g,
    classRegex = /(.[^s+>~.[:)]+)/g,
    pseudoElementRegex = /(::[^s+>~.[:]+|:first-line|:first-letter|:before|:after)/gi,
    pseudoClassWithBracketsRegex = /(:(?!not|global|local)[w-]+([^)]*))/gi,
    pseudoClassRegex = /(:(?!not|global|local)[^s+>~.[:]+)/g,
    elementRegex = /([^s+>~.[:]+)/g;
    findMatch = function(regex, type) {
    var matches, i, len, match, index, length;
    if (regex.test(selector)) {
    matches = selector.match(regex);
    for (i = 0, len = matches.length; i < len; i += 1) {
    typeCount[type] += 1;
    match = matches[i];
    index = selector.indexOf(match);
    length = match.length;
    parts.push({
    selector: input.substr(index, length),
    type: type,
    index: index,
    length: length
    });
    selector = selector.replace(match, Array(length + 1).join(' '));
    }
    }
    };
    (function() {
    var replaceWithPlainText = function(regex) {
    var matches, i, len, match;
    if (regex.test(selector)) {
    matches = selector.match(regex);
    for (i = 0, len = matches.length; i < len; i += 1) {
    match = matches[i];
    selector = selector.replace(match, Array(match.length + 1).join('A'));
    }
    }
    },
    escapeHexadecimalRegex = /\[0-9A-Fa-f]{6}s?/g,
    escapeHexadecimalRegex2 = /\[0-9A-Fa-f]{1,5}s/g,
    escapeSpecialCharacter = /\./g;
    replaceWithPlainText(escapeHexadecimalRegex);
    replaceWithPlainText(escapeHexadecimalRegex2);
    replaceWithPlainText(escapeSpecialCharacter);
    }());
    (function() {
    var regex = /{[^]*/gm,
    matches, i, len, match;
    if (regex.test(selector)) {
    matches = selector.match(regex);
    for (i = 0, len = matches.length; i < len; i += 1) {
    match = matches[i];
    selector = selector.replace(match, Array(match.length + 1).join(' '));
    }
    }
    }());
    findMatch(attributeRegex, 'b');
    findMatch(idRegex, 'a');
    findMatch(classRegex, 'b');
    findMatch(pseudoElementRegex, 'c');
    findMatch(pseudoClassWithBracketsRegex, 'b');
    findMatch(pseudoClassRegex, 'b');
    selector = selector.replace(/[*s+>~]/g, ' ');
    selector = selector.replace(/[#.]/g, ' ');
    selector = selector.replace(/:not/g, '    ');
    selector = selector.replace(/:local/g, '      ');
    selector = selector.replace(/:global/g, '       ');
    selector = selector.replace(/[()]/g, ' ');
    findMatch(elementRegex, 'c');
    parts.sort(function(a, b) {
    return a.index - b.index;
    });
    return [0, typeCount.a, typeCount.b, typeCount.c];
    };
  3. 比较优先级: 使用 compareSpecifities 函数比较两个 CSS 选择器的优先级。

    function compareSpecifities(aSpecificity, bSpecificity) {
    for (var i = 0; i < 4; i += 1) {
    if (aSpecificity[i] < bSpecificity[i]) {
    return false;
    } else if (aSpecificity[i] > bSpecificity[i]) {
    return true;
    }
    }
    return true;
    };
  4. 获取期望样式: 遍历完成后,givenStyles 对象将包含目标元素的期望样式。

    豆包爱学

    豆包爱学

    豆包旗下AI学习应用

    豆包爱学32

    查看详情
    豆包爱学

    let propertiesWeWant = ['width', 'height', 'background-color', 'color', 'margin', 'font-size'];
    let ele = document.querySelector(".hello");
    let givenStyles = getStylesFromStylesheets(ele);
    let propertyList = {};
    for(let i = 0; i < propertiesWeWant.length; i++){
    let property = propertiesWeWant[i];
    if(typeof(givenStyles[property]) !== "undefined"){
    propertyList[property] = givenStyles[property].style;
    }
    else{
    propertyList[property] = "";
    }
    }
    console.log(propertyList);

示例代码

<!DOCTYPE html>
<html>
<head>
<title>获取元素期望样式</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/<a class="__cf_email__" data-cfemail="96f4f9f9e2e5e2e4f7e6d6a5b8a2b8a7" href="https://www.php.cn/cdn-cgi/l/email-protection">[email protected]</a>/dist/css/bootstrap.min.css" >
<style>
blockquote{
margin:7px;
border-left: 10000px;
}
#hello-id.hello{
height: 1px;
}
#hello-id{
height: 2px;
}
html #hello-id{
height: 3px;
color: green;
}
#hello-id.hello{
height: 4px;
color: turquoise;
}
.hello-wrapper .hello {
height: 5px;
background-color: blue;
}
.hello {
height: 5px;
background-color: red;
}
#bogus.bogus{
height: 6px;
background-color: orange;
}
</style>
</head>
<body>
<div class="hello-wrapper">
<blockquote id="hello-id" class="hello">
helloInnerText
</blockquote>
</div>
<script>
let propertiesWeWant = ['width', 'height', 'background-color', 'color', 'margin', 'font-size'];
let ele = document.querySelector(".hello");
for(let i = 0; i < document.styleSheets.length; i++){
prepareStylesheet(document.styleSheets[i]);
}
setTimeout(function(){
let givenStyles = getStylesFromStylesheets(ele);
let propertyList = {};
for(let i = 0; i < propertiesWeWant.length; i++){
let property = propertiesWeWant[i];
if(typeof(givenStyles[property]) !== "undefined"){
propertyList[property] = givenStyles[property].style;
}
else{
propertyList[property] = "";
}
}
console.log(propertyList);
},2000);
function prepareStylesheet(sheet){
try {
sheet.cssRules;
} catch (e) {
if (e.name === 'SecurityError') {
let nextElement = sheet.ownerNode.nextSibling;
let parentNode = sheet.ownerNode.parentNode;
sheet.ownerNode.parentNode.removeChild(sheet.ownerNode);
fetch(sheet.href).then(resp => resp.text()).then(css => {
let style = document.createElement('style');
style.innerText = css;
if(nextElement === null){
parentNode.appendChild(style);
}
else{
parentNode.insertBefore(style, nextElement);
}
});
}
}
}
function getStylesFromStylesheets(elementToInvestigate) {
let givenStyles = {};
for (let i = 0; i < document.styleSheets.length; i++) {
let rules = document.styleSheets[i].cssRules;
for (let j = 0; j < rules.length; j++) {
let rule = rules[j];
if (typeof(rule.selectorText) === "undefined") {
continue;
}
let split = rule.selectorText.split(",");
for(let l = 0; l < split.length; l++){
let selector = split[l];
let elements = document.querySelectorAll(selector);
let applies = false;
for (let k = 0; k < elements.length; k++) {
if (elements[k] === elementToInvestigate) {
applies = true;
break;
}
}
if (applies === true) {
let styles = rule.style;
for (let k = 0; k < styles.length; k++) {
let styleName = styles[k];
let styleValue = styles[styleName];
let newSpecificity = calculateSingle( selector);
if (typeof(givenStyles[styleName]) !== "undefined") {
let earlierSelector = givenStyles[styleName].selector;
let earlierSpecificity = givenStyles[styleName].specificity;
let newHasMoreSpecificity = compareSpecifities( newSpecificity, earlierSpecificity);
if (newHasMoreSpecificity === true) {
givenStyles[styleName] = {
style: styleValue,
specificity: newSpecificity
}
}
} else {
givenStyles[styleName] = {
style: styleValue,
specificity: newSpecificity
}
}
}
}
}
}
if (elementToInvestigate.style.length > 0) {
for (let j = 0; j < elementToInvestigate.style.length; j++) {
let styleName = elementToInvestigate.style[j];
let styleValue = elementToInvestigate.style[styleName];
givenStyles[styleName] = {
style: styleValue,
specificity: [1, 1, 1, 1] //not needed here
}
}
}
}
return givenStyles;
}
function calculateSingle(input) {
var selector = input,
findMatch,
typeCount = {
'a': 0,
'b': 0,
'c': 0
},
parts = [],
attributeRegex = /([[^]]+])/g,
idRegex = /(#[^#s+>~.[:)]+)/g,
classRegex = /(.[^s+>~.[:)]+)/g,
pseudoElementRegex = /(::[^s+>~.[:]+|:first-line|:first-letter|:before|:after)/gi,
pseudoClassWithBracketsRegex = /(:(?!not|global|local)[w-]+([^)]*))/gi,
pseudoClassRegex = /(:(?!not|global|local)[^s+>~.[:]+)/g,
elementRegex = /([^s+>~.[:]+)/g;
findMatch = function(regex, type) {
var matches, i, len, match, index, length;
if (regex.test(selector)) {
matches = selector.match(regex);
for (i = 0, len = matches.length; i < len; i += 1) {
typeCount[type] += 1;
match = matches[i];
index = selector.indexOf(match);
length = match.length;
parts.push({
selector: input.substr(index, length),
type: type,
index: index,
length: length
});
selector = selector.replace(match, Array(length + 1).join(' '));
}
}
};
(function() {
var replaceWithPlainText = function(regex) {
var matches, i, len, match;
if (regex.test(selector)) {
matches = selector.match(regex);
for (i = 0, len = matches.length; i < len; i += 1) {
match = matches[i];
selector = selector.replace(match, Array(match.length + 1).join('A'));
}
}
},
escapeHexadecimalRegex = /\[0-9A-Fa-f]{6}s?/g,
escapeHexadecimalRegex2 = /\[0-9A-Fa-f]{1,5}s/g,
escapeSpecialCharacter = /\./g;
replaceWithPlainText(escapeHexadecimalRegex);
replaceWithPlainText(escapeHexadecimalRegex2);
replaceWithPlainText(escapeSpecialCharacter);
}());
(function() {
var regex = /{[^]*/gm,
matches, i, len, match;
if (regex.test(selector)) {
matches = selector.match(regex);
for (i = 0, len = matches.length; i < len; i += 1) {
match = matches[i];
selector = selector.replace(match, Array(match.length + 1).join(' '));
}
}
}());
findMatch(attributeRegex, 'b');
findMatch(idRegex, 'a');
findMatch(classRegex, 'b');
findMatch(pseudoElementRegex, 'c');
findMatch(pseudoClassWithBracketsRegex, 'b');
findMatch(pseudoClassRegex, 'b');
selector = selector.replace(/[*s+>~]/g, ' ');
selector = selector.replace(/[#.]/g, ' ');
selector = selector.replace(/:not/g, '    ');
selector = selector.replace(/:local/g, '      ');
selector = selector.replace(/:global/g, '       ');
selector = selector.replace(/[()]/g, ' ');
findMatch(elementRegex, 'c');
parts.sort(function(a, b) {
return a.index - b.index;
});
return [0, typeCount.a, typeCount.b, typeCount.c];
};
function compareSpecifities(aSpecificity, bSpecificity) {
for (var i = 0; i < 4; i += 1) {
if (aSpecificity[i] < bSpecificity[i]) {
return false;
} else if (aSpecificity[i] > bSpecificity[i]) {
return true;
}
}
return true;
};
</script>
</body>
</html>

注意事项

  • 性能: 遍历样式表并计算选择器优先级是一个耗时的操作,不建议在生产环境中使用。
  • 跨域: 需要处理跨域问题,确保能够访问外部样式表。
  • 复杂选择器: 对于非常复杂的 CSS 选择器,优先级计算可能存在误差。
  • 动态样式: 此方法主要针对静态 CSS 样式表,对于通过 JavaScript 动态添加的样式,可能无法准确获取。

总结

本教程介绍了一种获取元素期望 CSS 样式的方法,通过遍历样式表并结合 CSS 选择器优先级,可以提取开发者在样式表或内联样式中定义的原始样式。虽然该方法存在性能问题,但在某些特定场景下,例如需要分析元素样式来源或进行样式调试时,仍然具有一定的价值。 在实际应用中,需要根据具体情况权衡性能和准确性,选择合适的方案。

立即进入“豆包AI人工智官网入口”;

立即学习“豆包AI人工智能在线问答入口”;

相关标签:

css javascript java html js bootstrap node app ai win 跨域 JavaScript css NULL if for try catch Length function 对象 href 选择器 样式表 伪类 伪元素

大家都在看:

CSS技巧:精细控制连续元素的间距
CSS教程:精细控制连续元素的间距
CSS技巧:精确控制连续上标()元素的间距
减少连续 元素间距的CSS技巧
CSS技巧:优化连续上标元素间的间距
温馨提示: 本文最后更新于2025-09-15 05:52:21,某些文章具有时效性,若有错误或已失效,请在下方留言或联系在线客服
文章版权声明 1 本网站名称: 创客网
2 本站永久网址:https://new.ie310.com
1 本文采用非商业性使用-相同方式共享 4.0 国际许可协议[CC BY-NC-SA]进行授权
2 本站所有内容仅供参考,分享出来是为了可以给大家提供新的思路。
3 互联网转载资源会有一些其他联系方式,请大家不要盲目相信,被骗本站概不负责!
4 本网站只做项目揭秘,无法一对一教学指导,每篇文章内都含项目全套的教程讲解,请仔细阅读。
5 本站分享的所有平台仅供展示,本站不对平台真实性负责,站长建议大家自己根据项目关键词自己选择平台。
6 因为文章发布时间和您阅读文章时间存在时间差,所以有些项目红利期可能已经过了,能不能赚钱需要自己判断。
7 本网站仅做资源分享,不做任何收益保障,创业公司上收费几百上千的项目我免费分享出来的,希望大家可以认真学习。
8 本站所有资料均来自互联网公开分享,并不代表本站立场,如不慎侵犯到您的版权利益,请联系79283999@qq.com删除。

本站资料仅供学习交流使用请勿商业运营,严禁从事违法,侵权等任何非法活动,否则后果自负!
THE END
喜欢就支持一下吧
点赞12赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容