Better CSS detector

Once, many years ago, my team lead looked at my code astonished, and cried: You, the clean code man, written this? To here credit I will say that the code was poorly written. But my response was a paraphrase based on one of Uncle Bob’s clean code principles “How do you write clean code: you write a bad one, and then clean it. Obviously, this sentence earned me a place of honor in the bord of unforgettable statements.

Years passed since then, but the rule still applies, this is why once found out about the document.styleSheets API, I immediately when back and rewritten the unused CSS detector. Even that we currently don’t need it.

Though the trigger was using the actual CSS from the page, and not a preset hardcoded list, refactoring is a good opportunity to rethink each and every line:

  • Using document styleSheets API.
  • Changing from object to class.
  • Using arrow functions.
  • Better data objects.
  • Clearer code.
  • But most of all, this undesirable thing called: code elegancy.
class CssDetector {
  cycle = 0;
  csss = [];

  init = () => {
    for(let sId = 0; sId < document.styleSheets.length; sId ++){
      let ss = document.styleSheets[sId];
      try{
        for(let cId = 0; cId < ss.cssRules.length; cId ++){
          let cr = ss.cssRules[cId];
          const o = this.csss.find(x => x.selectorText === cr.selectorText);
          if(!o){
            this.csss.push(new Selector(cr.selectorText, sId,cId));
          }else{
            o.addSelector(sId,cId);
          }
        }
      }catch{}
    };
  }

  check = () => {
    this.csss.filter(e => e.isUsed === false).forEach(e => {
      if(document.querySelector(e.selectorText)){
        e.isUsed = true;
      }
    });
    if(this.csss.every(e => e.isUsed === true)){
      this.stop();
    }
    this.print();
  }

  print = () => {
    console.log('Cycle : ' + ++this.cycle);
    console.log(' Used : ' + this.csss.filter(e => e.isUsed === true).length);
    console.log(' Unused: ' + this.csss.filter(e => e.isUsed === false).length);
  }

  start = () => {
    console.log('cssDetector start');
    this.timeinterval = setInterval(this.check,500);
  }

  stop = ()=>{
    clearInterval(this.timeinterval);
    console.log('cssDetector stop');
  }
}

The CSS selector data object looks like this:

class Selector{
  selectorText;
  selector;
  isUsed = false;

  constructor(selectorText,styleSheetId, cssRuleId){
    this.selectorText = selectorText;
    this.selector = [[styleSheetId, cssRuleId]];
  }

  addSelector(styleSheetId, cssRuleId){
    this.selector.push([styleSheetId, cssRuleId])
  }
}

This piece of code gets all the CSS selectors from the document and mappes them to their style sheet. Then it runs each 500ms and checks if there’s a corresponding element on the DOM, until all the CSS selectors are used, or the APP is stopped manually.

Using the CSS detector is as simple as copying to the console:

const cssDetector = new CssDetector();
cssDetector.init()
cssDetector.start();
..
..
..

cssDetector.stop();

And s simple test can be done by creating a simple HTML page that has two selectors, one exists in the DOM and the second will be added after the detector runs. Something like the following code:

<html>
  <head>
    <script src='cssDetector.js'></script>
  </head>
  <body>
    <h1>red</h1>
    <div></div>
    <style>
      h1 {background-color:red;}
      h2 {background-color:green;}
    </style>
    <script>

      const cssDetector = new CssDetector();
      cssDetector.init()
      cssDetector.start();

      setTimeout(()=>{
        document.querySelector('div').innerHTML ='<h2>zxc</h2>';
      },1000);

      setTimeout(()=>{
        console.log('timeout');
      },4000);
    </script>
  </body>
</html>

All’s left is to watch the console and hope for some good statistics about the coronavirus that will let us out of this madness.