获取元素期望样式的教程

碧海醫心
发布: 2025-09-14 23:07:01
原创
833人浏览过

 获取元素期望样式的教程

本文旨在提供一种获取元素期望 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助手

    腾讯元宝 223
    查看详情 腾讯元宝
    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="/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" style="width:1px; background-color: pink;">
        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 选择器优先级,可以提取开发者在样式表或内联样式中定义的原始样式。虽然该方法存在性能问题,但在某些特定场景下,例如需要分析元素样式来源或进行样式调试时,仍然具有一定的价值。 在实际应用中,需要根据具体情况权衡性能和准确性,选择合适的方案。

登录后复制

以上就是获取元素期望样式的教程的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
热门推荐
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号