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.