요소에 적용되는 모든 CSS 규칙 찾기


87

많은 도구 / 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 # description 요소에는 빨간색과 20px의 글꼴 크기라는 두 가지 CSS 규칙이 적용됩니다.

이러한 계산 된 CSS 규칙의 출처를 찾고 싶습니다 (색상은 p 규칙 등).



브라우저에서보고 브라우저 개발자 도구 (예 : Chrome의 요소 탭)를 사용합니까?
Ronnie Royston

답변:


77

이 질문에는 현재 가볍고 (라이브러리가 아닌) 브라우저 간 호환 가능한 답변이 없으므로 하나를 제공하려고합니다.

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 : http://jsfiddle.net/HP326/6/

호출 css(document.getElementById('elementId'))하면 전달 된 요소와 일치하는 각 CSS 규칙에 대한 요소가있는 배열이 반환됩니다. 각 규칙에 대한 자세한 정보를 찾으려면 CSSRule 객체 문서를 확인하세요 .


1
a.matches이 줄에 정의되어 있습니다 : a.matches = a.matches || a.webkitMatchesSelector || a.mozMatchesSelector || a.msMatchesSelector || a.oMatchesSelector. 이는 DOM 노드에 대한 (표준) "matches"메소드가 이미있는 경우 해당 메소드를 사용하고, 그렇지 않으면 Webkit 특정 메소드 (webkitMatchesSelector)를 사용한 다음 Mozilla, Microsoft 및 Opera를 사용하려고합니다. 여기에서 자세한 내용을 읽을 수 있습니다. developer.mozilla.org/en/docs/Web/API/Element/matches
SB

3
불행히도이 대안은 자식의 부모 요소에서 계단식으로 연결된 모든 CSS 규칙을 감지하지 못한다고 생각합니다. Fiddle : jsfiddle.net/t554xo2L 이 경우 UL 규칙 (요소에 적용됨)이 if (a.matches(rules[r].selectorText))보호 조건 과 일치하지 않습니다 .
funforums

2
나는 그것이 / inherited / CSS 규칙을 나열했다고 주장한 적이 없습니다. 전달 된 요소와 일치하는 CSS 규칙을 나열하는 것뿐입니다. 해당 요소에 대한 상속 된 규칙도 얻으려면 DOM을 위쪽으로 탐색하고 css()각 부모 요소를 호출해야합니다 .
SB

2
나는 알고 있습니다 :-)이 질문을 살펴볼 수있는 사람들은 질문의 제목이 말하는 것처럼 '요소에 적용되는 모든 CSS 규칙'을 얻는다고 가정 할 수 있기 때문에 이것을 지적하고 싶었습니다. .
funforums

3
상속 된 규칙을 포함하여 현재 모든 규칙이 요소에 적용되도록하려면 getComputedStyle을 사용해야합니다. 그 점에 비추어 볼 때이 대답은 정확하고 부모로부터 상속 된 스타일 (예 : 부모에게 할당 된 텍스트 색상)을 포함하지 않는 것이 옳다고 생각합니다. 하지만 여기에 포함되지 않은 것은 미디어 쿼리와 함께 조건부로 적용되는 규칙입니다.
tremby

23

편집 :이 답변은 이제 더 이상 사용되지 않으며 더 이상 Chrome 64 이상에서 작동하지 않습니다 . 역사적 맥락으로 떠나기. 사실 버그 보고서는 이것을 사용하는 대체 솔루션을 위해이 질문으로 다시 연결됩니다.


한 시간의 연구 끝에 내 질문에 답할 수 있었던 것 같습니다.

다음과 같이 간단합니다.

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

(WebKit / Chrome, 아마도 다른 것에서도 작동)


4
크롬에서만 지원되는 경우에는 많이 사용되지 않습니다. 모든 방문자의 5 % 미만 (인구 통계에 따라 다름)에게 적용됩니다.
Tomasi

5
@diamandiev : 2012 년 6 월 현재 Chrome 사용 점유율이 32 % 이상으로 증가했습니다 (IE 사용보다 약간 높습니다!). gs.statcounter.com
Roy Tinker

6
getMatchedCSSRules는 요소에 적용되는 최종 스타일을 표시하지 않습니다. 나타나는 순서대로 적용되는 모든 CSSStyleRule 개체의 배열을 반환합니다. CSS 미디어 쿼리를 통해 반응 형 웹 디자인을 수행하거나 둘 이상의 스타일 시트 (예 : IE 용)를로드하는 경우에도 반환 된 각 스타일을 반복하고 각 규칙에 대한 CSS 특이성을 계산해야합니다. 그런 다음 적용되는 최종 규칙을 계산합니다. 브라우저가 자연스럽게하는 일을 재현해야합니다. 예제에서이를 증명하려면 스타일 선언의 시작 부분에 "p {color : blue! important}"를 추가하십시오.
mrbinky3000

24
현재 Chrome 41에서 더 이상 사용되지 않습니다 . code.google.com/p/chromium/issues/detail?id=437569#c2를 참조하세요 .
Daniel Darabos

5
이것은 마침내 Chrome 63에서 제거 되었습니다 (공식 블로그 게시물-이 질문을 다시 가리키고 있음)
brichins

19

요청 된 작업을 수행하는이 라이브러리를 살펴보십시오. http://www.brothercake.com/site/resources/scripts/cssutilities/

IE6로 돌아가는 모든 최신 브라우저에서 작동하며 Firebug와 같은 규칙 및 속성 컬렉션을 제공 할 수 있으며 (사실 Firebug보다 더 정확함) 모든 규칙의 상대적 또는 절대적 특이성을 계산할 수도 있습니다. 유일한주의 사항은 정적 미디어 유형은 이해하지만 미디어 쿼리는 이해하지 못한다는 것입니다.


이 모듈은 정말 훌륭합니다. 저자로부터 더 많은 사랑을 받기를 바랍니다.
mr1031011

17

짧은 버전 2017 년 4 월 12 일

도전자가 나타납니다.

var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => 
    [].concat(...[...css].map(s => [...s.cssRules||[]])) /* 1 */
    .filter(r => el.matches(r.selectorText));            /* 2 */

Line /* 1 */은 모든 규칙의 평면 배열을 만듭니다.
라인 /* 2 */은 일치하지 않는 규칙을 버립니다.

동일한 페이지에서 @SB의 기능css(el) 을 기반으로 합니다 .

예 1

var div = iframedoc.querySelector("#myelement");
var rules = getMatchedCSSRules(div, iframedoc.styleSheets);
console.log(rules[0].parentStyleSheet.ownerNode, rules[0].cssText);

예 2

var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => 
    [].concat(...[...css].map(s => [...s.cssRules||[]]))
    .filter(r => el.matches(r.selectorText));

function Go(big,show) {
    var r = getMatchedCSSRules(big);
PrintInfo:
    var f = (dd,rr,ee="\n") => dd + rr.cssText.slice(0,50) + ee;
    show.value += "--------------- Rules: ----------------\n";
    show.value += f("Rule 1:   ", r[0]);
    show.value += f("Rule 2:   ", r[1]);
    show.value += f("Inline:   ", big.style);
    show.value += f("Computed: ", getComputedStyle(big), "(…)\n");
    show.value += "-------- Style element (HTML): --------\n";
    show.value += r[0].parentStyleSheet.ownerNode.outerHTML;
}

Go(...document.querySelectorAll("#big,#show"));
.red {color: red;}
#big {font-size: 20px;}
<h3 id="big" class="red" style="margin: 0">Lorem ipsum</h3>
<textarea id="show" cols="70" rows="10"></textarea>

단점

  • 미디어 처리 없음 @import ,, @media.
  • 교차 도메인 스타일 시트에서로드 된 스타일에 액세스 할 수 없습니다.
  • 선택기 "특이성"(중요도 순서)으로 정렬하지 않습니다.
  • 부모로부터 상속 된 스타일이 없습니다.
  • 오래되거나 초보적인 브라우저에서는 작동하지 않을 수 있습니다.
  • 의사 클래스 및 의사 선택기에 어떻게 대처하는지 확실하지 않지만 괜찮은 것 같습니다.

아마도 언젠가는 이러한 단점을 해결할 것입니다.

긴 버전 2018 년 8 월 12 일

다음은 누군가의 GitHub 페이지 에서 가져온 훨씬 더 포괄적 인 구현입니다 (이 원본 코드 에서 Bugzilla 를 통해 분기 됨 ). Gecko 및 IE 용으로 작성되었지만 Blink에서도 작동한다는 소문이 있습니다.

2017 년 5 월 4 일 : 특이성 계산기에 중요한 버그가 있었지만 이제 수정했습니다. (저는 GitHub 계정이 없기 때문에 저자에게 알릴 수 없습니다.)

2018 년 8 월 12 일 : 최근 Chrome 업데이트는 this독립 변수에 할당 된 메서드에서 개체 범위 ( )를 분리 한 것 같습니다 . 따라서 호출 matcher(selector)이 작동을 멈췄습니다. 로 교체하면 matcher.call(el, selector)문제가 해결되었습니다.

// polyfill window.getMatchedCSSRules() in FireFox 6+
if (typeof window.getMatchedCSSRules !== 'function') {
    var ELEMENT_RE = /[\w-]+/g,
            ID_RE = /#[\w-]+/g,
            CLASS_RE = /\.[\w-]+/g,
            ATTR_RE = /\[[^\]]+\]/g,
            // :not() pseudo-class does not add to specificity, but its content does as if it was outside it
            PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g,
            PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g;
        // convert an array-like object to array
        function toArray(list) {
            return [].slice.call(list);
        }

        // handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same
        function getSheetRules(stylesheet) {
            var sheet_media = stylesheet.media && stylesheet.media.mediaText;
            // if this sheet is disabled skip it
            if ( stylesheet.disabled ) return [];
            // if this sheet's media is specified and doesn't match the viewport then skip it
            if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return [];
            // get the style rules of this sheet
            return toArray(stylesheet.cssRules);
        }

        function _find(string, re) {
            var matches = string.match(re);
            return matches ? matches.length : 0;
        }

        // calculates the specificity of a given `selector`
        function calculateScore(selector) {
            var score = [0,0,0],
                parts = selector.split(' '),
                part, match;
            //TODO: clean the ':not' part since the last ELEMENT_RE will pick it up
            while (part = parts.shift(), typeof part == 'string') {
                // find all pseudo-elements
                match = _find(part, PSEUDO_ELEMENTS_RE);
                score[2] += match;
                // and remove them
                match && (part = part.replace(PSEUDO_ELEMENTS_RE, ''));
                // find all pseudo-classes
                match = _find(part, PSEUDO_CLASSES_RE);
                score[1] += match;
                // and remove them
                match && (part = part.replace(PSEUDO_CLASSES_RE, ''));
                // find all attributes
                match = _find(part, ATTR_RE);
                score[1] += match;
                // and remove them
                match && (part = part.replace(ATTR_RE, ''));
                // find all IDs
                match = _find(part, ID_RE);
                score[0] += match;
                // and remove them
                match && (part = part.replace(ID_RE, ''));
                // find all classes
                match = _find(part, CLASS_RE);
                score[1] += match;
                // and remove them
                match && (part = part.replace(CLASS_RE, ''));
                // find all elements
                score[2] += _find(part, ELEMENT_RE);
            }
            return parseInt(score.join(''), 10);
        }

        // returns the heights possible specificity score an element can get from a give rule's selectorText
        function getSpecificityScore(element, selector_text) {
            var selectors = selector_text.split(','),
                selector, score, result = 0;
            while (selector = selectors.shift()) {
                if (matchesSelector(element, selector)) {
                    score = calculateScore(selector);
                    result = score > result ? score : result;
                }
            }
            return result;
        }

        function sortBySpecificity(element, rules) {
            // comparing function that sorts CSSStyleRules according to specificity of their `selectorText`
            function compareSpecificity (a, b) {
                return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText);
            }

            return rules.sort(compareSpecificity);
        }

        // Find correct matchesSelector impl
        function matchesSelector(el, selector) {
          var matcher = el.matchesSelector || el.mozMatchesSelector || 
              el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector;
          return matcher.call(el, selector);
        }

        //TODO: not supporting 2nd argument for selecting pseudo elements
        //TODO: not supporting 3rd argument for checking author style sheets only
        window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) {
            var style_sheets, sheet, sheet_media,
                rules, rule,
                result = [];
            // get stylesheets and convert to a regular Array
            style_sheets = toArray(window.document.styleSheets);

            // assuming the browser hands us stylesheets in order of appearance
            // we iterate them from the beginning to follow proper cascade order
            while (sheet = style_sheets.shift()) {
                // get the style rules of this sheet
                rules = getSheetRules(sheet);
                // loop the rules in order of appearance
                while (rule = rules.shift()) {
                    // if this is an @import rule
                    if (rule.styleSheet) {
                        // insert the imported stylesheet's rules at the beginning of this stylesheet's rules
                        rules = getSheetRules(rule.styleSheet).concat(rules);
                        // and skip this rule
                        continue;
                    }
                    // if there's no stylesheet attribute BUT there IS a media attribute it's a media rule
                    else if (rule.media) {
                        // insert the contained rules of this media rule to the beginning of this stylesheet's rules
                        rules = getSheetRules(rule).concat(rules);
                        // and skip it
                        continue
                    }

                    // check if this element matches this rule's selector
                    if (matchesSelector(element, rule.selectorText)) {
                        // push the rule to the results set
                        result.push(rule);
                    }
                }
            }
            // sort according to specificity
            return sortBySpecificity(element, result);
        };
}

버그 수정

  • = match+= match
  • return re ? re.length : 0;return matches ? matches.length : 0;
  • _matchesSelector(element, selector)matchesSelector(element, selector)
  • matcher(selector)matcher.call(el, selector)

getSheetRules에서 if (stylesheet.cssRules === null) {return []}을 추가해야 작동합니다.
Gwater17

"긴 버전"을 테스트했습니다. 나를 위해 작동합니다. 너무 나쁜 getMatchedCSSRules ()는 브라우저에 의해 표준화되지 않았습니다.
colin moock 19

이것은 h1 및 h1, div와 같은 동일한 특이성을 가진 두 개의 선택기를 어떻게 처리합니까? 마지막으로 선언 된 것을 사용해야하는 곳?
Stellan

아마도 여기서 의사 처리에 대한 아이디어를 얻을 수 있습니까? github.com/dvtng/jss/blob/master/jss.js
mr1031011

4

다음은 일치하는 미디어 쿼리 내에서 일치하는 규칙을 반환하는 SB의 답변 버전입니다. 나는 *.rules || *.cssRules합체와 .matches구현 파인더를 제거했습니다 . 폴리 필을 추가하거나 필요한 경우 해당 라인을 다시 추가하십시오.

이 버전 CSSStyleRule은 규칙 텍스트가 아닌 객체 도 반환합니다 . 규칙의 세부 사항을 이런 식으로 프로그래밍 방식으로 더 쉽게 조사 할 수 있기 때문에 이것이 좀 더 유용하다고 생각합니다.

커피:

getMatchedCSSRules = (element) ->
  sheets = document.styleSheets
  matching = []

  loopRules = (rules) ->
    for rule in rules
      if rule instanceof CSSMediaRule
        if window.matchMedia(rule.conditionText).matches
          loopRules rule.cssRules
      else if rule instanceof CSSStyleRule
        if element.matches rule.selectorText
          matching.push rule
    return

  loopRules sheet.cssRules for sheet in sheets

  return matching

JS :

function getMatchedCSSRules(element) {
  var i, len, matching = [], sheets = document.styleSheets;

  function loopRules(rules) {
    var i, len, rule;

    for (i = 0, len = rules.length; i < len; i++) {
      rule = rules[i];
      if (rule instanceof CSSMediaRule) {
        if (window.matchMedia(rule.conditionText).matches) {
          loopRules(rule.cssRules);
        }
      } else if (rule instanceof CSSStyleRule) {
        if (element.matches(rule.selectorText)) {
          matching.push(rule);
        }
      }
    }
  };

  for (i = 0, len = sheets.length; i < len; i++) {
    loopRules(sheets[i].cssRules);
  }

  return matching;
}

통과 된 아이들 element에게도 사용되도록 어떻게 변경할 수 있습니까?
Kragalon 2016.04.09

2
사용 사례는 무엇입니까? 아이들에게 적용되는 규칙이 반드시 부모에게 적용되는 것은 아니기 때문에 그것이 어디에서 유용 할 지 실제로는 알지 못합니다. 특별한 공통점이없는 규칙 더미로 끝날 것입니다. 정말로 원한다면 아이들을 재귀하고 각각에 대해이 메서드를 실행하고 모든 결과의 배열을 구축 할 수 있습니다.
tremby

나는 cloneNode(true)기능 을 만들려고 노력하고 있지만 스타일링도 깊게 복제했습니다.
Kragalon 2016

1
이 조건 : if (window.matchMedia (rule.conditionText) .matches) {...} "rule.conditionText"가 정의되지 않았기 때문에 제 경우에는 일치를 막았습니다. 그것 없이는 작동했습니다. news.ycombinator.com에서 이것을 시도하고 테스트 할 수 있습니다 . "span.pagetop b"에 귀하의 기능과 일치하지 않는 미디어 쿼리 규칙이 있습니다.
ayal gelles

1
Chrome은 CSSMediaRule 인스턴스에서 conditionText 속성을 지원하지 않습니다.
Macil

3

다음은 쿼리 getMatchedCSSRules를 지원하는 내 버전의 함수입니다 @media.

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
}

1

var GetMatchedCSSRules = (elem, css = document.styleSheets) => Array.from(css)
  .map(s => Array.from(s.cssRules).filter(r => elem.matches(r.selectorText)))
  .reduce((a,b) => a.concat(b));

function Go(paragraph, print) {
  var rules = GetMatchedCSSRules(paragraph);
PrintInfo:
  print.value += "Rule 1: " + rules[0].cssText + "\n";
  print.value += "Rule 2: " + rules[1].cssText + "\n\n";
  print.value += rules[0].parentStyleSheet.ownerNode.outerHTML;
}

Go(document.getElementById("description"), document.getElementById("print"));
p {color: red;}
#description {font-size: 20px;}
<p id="description">Lorem ipsum</p>
<textarea id="print" cols="50" rows="12"></textarea>


3
내 대답 의 이전 버전 의 무의미한 복제 . 페이지를 오염시키는 것뿐입니다. 완전한 최신 버전 : 여기 .
7vujy0f0hy

1

IE9 +를 보장하면서 요청 된 요소와 그 자식에 대한 CSS를 계산하는 함수를 작성하고 필요한 경우 아래 스 니펫에서 새 className에 저장할 수 있습니다.

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

2
1. 전체 computeStyles서브 루틴을 el => getComputedStyle(el).cssText. 증거 : 바이올린 . 2. '.' + element.className 하나의 클래스 이름이 있다고 가정하기 때문에 잘못된 구성입니다. 유효한 구성은입니다 element.className.replace(/^| /g, '.'). 3. 함수는 클래스 이외의 다른 CSS 선택 자의 가능성을 무시합니다. 4. 귀하의 재귀는 임의로 한 수준으로 제한됩니다 (자녀이지만 손자는 아님). 5. 사용법 : 없음 getElementByClassName, 만 getElementsByClassName(배열 반환).
7vujy0f0hy

1

이 시점에서 SB의 답변이 받아 들여 져야한다고 생각하지만 정확하지는 않습니다. 놓칠 수있는 몇 가지 규칙이있을 것이라고 몇 번 언급됩니다. 이에 직면하여 element.matches 대신 document.querySelectorAll을 사용하기로 결정했습니다. 유일한 것은 원하는 요소와 비교하기 위해 일종의 고유 한 요소 식별이 필요하다는 것입니다. 대부분의 경우 id를 고유 한 값으로 설정하면 가능하다고 생각합니다. 이것이 당신이 일치하는 요소를 식별하는 방법입니다. document.querySelectorAll의 결과를 찾고있는 요소와 일치시키는 일반적인 방법을 생각할 수 있다면 본질적으로 getMatchedCSSRules의 완전한 폴리 필이 될 것입니다.

document.querySelectorAll의 성능을 확인했는데 element.matches보다 느릴 수 있지만 대부분의 경우 문제가되지 않습니다. 0.001 밀리 초 정도 걸립니다.

나는 또한 이것을 할 수 있다고 광고하는 CSSUtilities 라이브러리를 찾았지만 오래되고 한동안 업데이트되지 않았다고 생각합니다. 소스 코드를 보면 놓친 경우가있을 수 있다고 생각합니다.


CSSUtilities는 정말 오래되었지만 의사 상태에 대한 규칙도 반환합니다 (예 : 호버 규칙을 반환 할 수 있음). 나는 아직 의사 상태를 다루는 답을 찾지 못했습니다.
mr1031011
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.