Skip to content

Instantly share code, notes, and snippets.

@eriktorsner
Forked from james-Ballyhoo/critcss.snippet.js
Last active November 11, 2016 11:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eriktorsner/a9baa22b231ed46d06605347ac8787bb to your computer and use it in GitHub Desktop.
Save eriktorsner/a9baa22b231ed46d06605347ac8787bb to your computer and use it in GitHub Desktop.
Based off of https://gist.github.com/PaulKinlan/6284142 , but with support for media queries and dumps css to a textarea in a panel stuck to the top of the page. Only tested in Chrome, uses height of window to determine "critical path".
(function(){
if(document.querySelector("#_CRIT_CSS")){return;}
var container = document.createElement("div");
container.id = "_CRIT_CSS";
container.innerHTML = '<textarea cols=80 rows=20></textarea><button id="CRIT_FIND">Find Critical CSS</button><button id="CRIT_CLOSE">Exit</button>';
container.style.position = "fixed";
container.style.top = 0;
container.style.left = 0;
container.style.right = 0;
container.style.backgroundColor = "#FFF";
container.style.zIndex = 99999999999999;
document.body.appendChild(container);
container.querySelector("#CRIT_FIND").addEventListener("click",function(){
container.querySelector("textarea").value = findCriticalCSS(window,document);
});
container.querySelector("#CRIT_CLOSE").addEventListener("click",function(){
container.remove();
container = null;
});
function findCriticalCSS(w, d){
//Setup our Pseudo selector killer, view height and critical nodes
var removePseudo = /([^\s,\:\(])\:\:?(?!not)[a-zA-Z\-]{1,}(?:\(.*?\))?/g;
var height = w.innerHeight;
var criticalNodes = [];
//Go find all the critical nodes
var walker = d.createTreeWalker(d, NodeFilter.SHOW_ELEMENT, function(node) { if(node === container){console.error("FOUND IT", node)};return node === container ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT; }, true);
while(walker.nextNode()) {
var node = walker.currentNode;
//var rect = node.getBoundingClientRect();
if(node.offsetTop < height) {
criticalNodes.push(node);
}
}
console.log("Found " + criticalNodes.length + " critical nodes");
//Grab loaded stylesheets
var sheets = document.styleSheets;
var outCss = Array.prototype.map.call(sheets,function(sheet){
var rules = sheet.rules || sheet.cssRules;
//If there are rules
if(rules){
return {
sheet: sheet,
rules: Array.prototype.map.call(rules, function(rule){ //Convert each CSSRule into a
try{
if(rule instanceof CSSMediaRule){
var subRules = rule.rules || rule.cssRules;
var css = Array.prototype.filter.call(subRules, function(rule){
return criticalNodes.filter(function(e){ return e.matches(rule.selectorText.replace(removePseudo,"$1"))}).length > 0;
}).map(function(rule){return rule.cssText}).reduce(function(ruleCss,init){return init + "\n" + ruleCss;},"");
return css ? ("@media " + rule.media.mediaText + " { " + css + "}") : null;
}else if(rule instanceof CSSStyleRule){
return criticalNodes.filter(function(e){ return e.matches(rule.selectorText.replace(removePseudo,"$1"))}).length > 0 ? rule.cssText : null;
}else{
console.warn("allowing", rule);
return rule.cssText;
}
}catch(e){
console.error("Bad CSS rule", rule.selectorText);
throw e;
}
}).filter(function(e){return e;})
}
}else{
return null;
}
}).filter(function(cssEntry){return cssEntry && cssEntry.rules.length > 0})
//Enable only this for debug dump
//.map(function(e){return {href: e.sheet.href, oldsize: e.sheet.cssRules.length, rules: e.rules, css: e.rules.join("\n")} })
//Enable this for CSS
.map(function(cssEntry){ return cssEntry.rules.join(""); })
.reduce(function(css,out){return out + css},"")
return outCss.replace(/\n/g,"").replace(/content\: \"(.)\"/g,function(a,e){
return "content: \"\\" + escape(e).substr(2) + "\"";
});
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment