5

With the help of this answer (get all css classes background & color properties values) I can get all CSS classes background & color properties values. It works when I use internal css codes.

Now, I have more than 35 external css stylesheets. When I tried the below code, It returns first external CSS file output only. Unable to get remaining stylesheet background & color properties values. How do I access remaining stylesheets?

$('#nightMode').on('click', function() {
    // var color = $("body").css("background");
    // var test = invertColor("#00a3fe"); 
    // console.log(color); 

    let styles = document.styleSheets;

    const rgbToHex = (rgbStr) => !rgbStr ? '':'#' + rgbStr
      .slice(4,-1)
      .split(', ')
      .map(x => (+x).toString(16).padStart(2, '0'))
      .join('');

    let cssArr =[...styles[0].cssRules].map(x=> ({
      class: x.selectorText,
      background: rgbToHex(x.style.background),
      color: rgbToHex(x.style.color)
    } ));

    let genCssStr='';
    genCssStr+= '<style> \n\n';
    cssArr.forEach(x=> genCssStr+=`${x.class}{\n` + 
      (x.background ? `  background:${invertColor(x.background)};\n` : '') + 
      (x.color ? `  color:${invertColor(x.color)};\n` : '') + `}\n\n`);
    genCssStr+= '</style>';

    console.log(styles);
    console.log(genCssStr);
    $(genCssStr).appendTo("body");
    // console.log("array:", JSON.stringify(cssArr));
    // console.log("text:\n\n", genCssStr);
});
Salman A
  • 262,204
  • 82
  • 430
  • 521
Karuppiah RK
  • 3,894
  • 9
  • 40
  • 80
  • 4
    *"...Now, I have more than 35 external css stylesheets."* Yikes! I've seen bloated CMSs and WordPuke sites with 5 to 8 stylesheets at the most. You don't need to be mucking around with CSSRules you need to condense and refactor. – zer00ne Feb 20 '19 at 11:04
  • 1
    Have you looked at what `[...styles[0].cssRules]` looks like in console? It looks dubious. Try this: `let cssBloatedArray = styles.cssRules` then `[...cssBloatedArray]` – zer00ne Feb 20 '19 at 11:10
  • @zer00ne `[...styles[0].cssRules]` it returns this in console `Array(6) [ CSSFontFaceRule, CSSFontFaceRule, CSSFontFaceRule, CSSFontFaceRule, CSSFontFaceRule, CSSStyleRule ".massitems a" ]` – Karuppiah RK Feb 20 '19 at 11:19
  • @zer00ne `let cssBloatedArray = styles.cssRules` then `[...cssBloatedArray]` this one returns `TypeError: cssBloatedArray is undefined` – Karuppiah RK Feb 20 '19 at 11:21
  • @zer00ne `let cssBloatedArray = styles[0].cssRules` then `[...cssBloatedArray]` this returns `Array(6) [ CSSFontFaceRule, CSSFontFaceRule, CSSFontFaceRule, CSSFontFaceRule, CSSFontFaceRule, CSSStyleRule ".massitems a" ]` – Karuppiah RK Feb 20 '19 at 11:22
  • So `let styles = document.styleSheets.length;` is only 6 not 35? – zer00ne Feb 20 '19 at 11:49
  • Ok I see you are just adding a ` – zer00ne Feb 20 '19 at 11:52
  • This looks like an unusable object reference: `CSSFontFaceRule` and this looks like a CSS selector: `".massitems a" `. I don't think those are stylesheets? – zer00ne Feb 20 '19 at 11:58
  • @zer00ne No. `let styles = document.styleSheets.length;` it shows 35 – Karuppiah RK Feb 20 '19 at 13:00
  • Ok the `styles[0]` is only the first stylesheet of 35. You need to loop through the whole `styles` array: `styles[i]` – zer00ne Feb 20 '19 at 13:12
  • You need to iterate like this: `[...document.styleSheets].forEach(ss => ss.cssRules.doSomething());` but then you will run into another issue, that of stylesheets being loded from somewhere else. – Salman A Feb 26 '19 at 07:16
  • @KaruppiahRK the bounty is about to expire... have you not made up your mind? – Salman A Mar 01 '19 at 10:13
  • @SalmanA I have been little bit busy in another work. So, I couldn't tried all these 3 below answers. I will upvote and accept the answer within next week. Sorry.. :-( – Karuppiah RK Mar 01 '19 at 10:16

3 Answers3

7

The primary issue is that you are iterating the first stylesheet only. You need to iterate all stylesheets. Also note that you cannot enumerate cssRules property of cross-domain stylesheets.

The following code processes all css rules inside all stylesheets (<link rel> and <style>). The try...catch block is added to ignore DOMExceptions if the external stylesheet cannot be accessed:

var cssArr = [];
[...document.styleSheets].forEach(function(styleSheet) {
  var cssRules;
  try {
    cssRules = styleSheet.cssRules;
  } catch (e) {
    console.log("Cannot process " + styleSheet.href);
    return;
  }
  [...cssRules].forEach(function(cssRule) {
    cssArr.push({
      class: cssRule.selectorText,
      background: cssRule.style.background,
      color: cssRule.style.color
    });
  });
});
console.log(cssArr);
Salman A
  • 262,204
  • 82
  • 430
  • 521
  • it throws `Cannot Process style.css`. Is there any way to access css classes and its properties? – Karuppiah RK Mar 15 '19 at 07:17
  • But previously, I can able to get the css classes and its properties of first stylesheet alone. – Karuppiah RK Mar 15 '19 at 07:18
  • If "style.css" belongs to another domain e.g. your webpage is http://example.com/blog and stylesheet is coming from http://code.jquery.com/themes/1/styles.css then no, there is no way (that I am aware of). – Salman A Mar 15 '19 at 07:42
  • No. All of my stylsheets are from same domain. Like css/main.css, css/style.css, setup.css. I don't have any stylesheets that belongs to another domain. – Karuppiah RK Mar 15 '19 at 09:27
  • Comment out the `try ... catch` and post the actual error you're getting. – Salman A Mar 15 '19 at 10:04
  • `Uncaught TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))` – Karuppiah RK Mar 15 '19 at 10:39
  • That is weird. You'll need to console.log the object that throws this error. Could be an imported stylesheet, an `@keyframe` rule, `@media` query etc. – Salman A Mar 15 '19 at 10:44
6

You are accessing only the first external file as you are using only the index 0: styles[0].cssRules. You should loop through all files like this:

styles.forEach(style => {
  let cssArr =[...style.cssRules].map(x=> ({ class: x.selectorText, background: rgbToHex(x.style.background), color: rgbToHex(x.style.color)} ));
})
vmf91
  • 1,897
  • 19
  • 27
2

This answer describes why some rules may not be able to be accessed.

The long and short is that if the css resources are cross domain, then security restrictions prevent access.

Additionally, within that answer, there is this reference which describes the style sheet interface and explicitly states the above assertion where cross origin resources can't be accessed, and this applies to cssRules, among some other properties on the document.styleSheets object.

The below code will catch that error gracefully and print the output of the error, or the cssRule to the console.

for (let i in [...document.styleSheets]){
    try{
        console.log(styles[i]['cssRules']);
    }catch(e){
        console.log(e);
    }
}    
Nik B
  • 136
  • 5