1

Is it possible to get all CSS styles that are applied to a given HTML element either in its styles property/attribute or via CSS selectors?

I'm looking for the computed styles, but only those styles that are set on the element, not all existing styles that can possibly be set for the element.

Here is an example of what DOESN'T work.

let span = document.querySelector('span') ;
let compStyles = getComputedStyle(span) ;

console.log('The desired result is: "color: blue; font-style: italic"' ) ;
console.log('But instead we get: ', compStyles.cssText ) ;
div {color: red; font-weight: bold}
span {color: blue}
<div>
Hello <span style="font-style: italic">world</span>
</div>

getComputedStyle gives a huge list of unneeded stuff. I'm only looking for the styles that are being applied directly to the element.

For example, DevTools shows at the top...
(1) the effective styles applied to the element, and below that it shows...
(2) the styles inherited from the parent. Then on a different tab, it shows...
(3) all computed styles.

I'm looking for number (1) only.


Why I need this.

When you select text in a page and copy that text, Chrome puts in the clipboard HTML code that looks like this:

<span style="color: rgb(255, 0, 0); font-family: 'Times New Roman'; font-size: medium; font-style: normal; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; display: inline !important;">
    Hello
</span>
<span style="color: blue; font-family: 'Times New Roman'; font-size: medium; font-variant: normal; font-weight: bold; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; font-style: italic;">
    world
</span>

i.e. every element has all its needed styles inline in its style attribute. That way the HTML code can be pasted anywhere else and it will look the same.
What Chrome is doing is to make the HTML code independent of any style sheets that may exist in the source page.

This is what I'm trying to do. I want to take a section of the page and generate it's equivalent HTML code with all its necessary styles integrated in the HTML itself.

What I want to avoid, though, is that the resulting HTML ends up being ridiculously big.
If I just take the result of getComputedStyle for every element, the final HTML will be a gigantic string.

Chrome does a good job at embedding the "important" styles (those that matter), instead of embedding literally every possible style property on every HTML element.

Seth
  • 83
  • 7
  • 1
    I'd say it's unlikely. With that in mind, why do you need this? There may be another way to achieve the result you want – Phil Apr 08 '20 at 23:08
  • 1
    be aware that an applied style may *not* apply due to specificity or other stuff – Temani Afif Apr 08 '20 at 23:09
  • It appears possible, [see here](https://stackoverflow.com/a/42069075/848250), I was able in a controlled environment to get that to work (see answer below). Although it seems Chrome now doesn't allow read access to the 'cssRules' property from 'CSSStyleSheet'. – Luís Ramalho Apr 08 '20 at 23:25
  • Or try this: https://stackoverflow.com/questions/9430659/how-to-get-all-the-applied-styles-of-an-element-by-just-giving-its-id – ikiK Apr 08 '20 at 23:29
  • @ikiK, hmm I've just tried that and it gives all the styles and not just the ones the user applied. Identical to the second output of the OP. – Luís Ramalho Apr 08 '20 at 23:32
  • This seems like a good read. https://zellwk.com/blog/css-values-in-js/ its year old so probably current. – ikiK Apr 08 '20 at 23:46

1 Answers1

1

In general it appears possible, however this does make it seem that it won't be possible in Chrome.

TL;DR: As of Chrome 64 you'll need to use a local development server to test functionality that depends on the CSS Object Model.

This snippet below is using this code, with some small changes for the case of the OP.

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({
        text: e.cssText.match(/\w\s{\s(.*?)}/)[1]
      })
    }
  }

  if (elm.getAttribute('style')) {
    rules.push({
      text: elm.getAttribute('style')
    })
  }
  return rules;
}

var styleSheetList = document.styleSheets;

let span = document.querySelector('span');

var rules = getAppliedCss(span);

console.log(rules.map(r => r.text).join(''));
div {color: red; font-weight: bold}
span {color: blue}
<div>
  Hello <span style="font-style: italic">world</span>
</div>
Luís Ramalho
  • 10,018
  • 4
  • 52
  • 67