Skip to content

Instantly share code, notes, and snippets.

@james-Ballyhoo
Created September 18, 2015 08:34
Show Gist options
  • Save james-Ballyhoo/04761ed2a5778c505527 to your computer and use it in GitHub Desktop.
Save james-Ballyhoo/04761ed2a5778c505527 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(rect.top < 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) + "\"";
});
}
})()
@charmcat
Copy link

I have the same issue. When I hit the button, noting happens.

@sasoriza
Copy link

sasoriza commented Jan 25, 2017

Nothing happening here either...
Tested with FF 50 and Chrome 55

@gnanet
Copy link

gnanet commented Aug 28, 2017

This is what i found in the console after try

critcss.snippet.js:1 undefined
critcss.snippet.js:31 FOUND IT <div id=​"_CRIT_CSS" style=​"position:​ fixed;​ top:​ 0px;​ left:​ 0px;​ right:​ 0px;​ background-color:​ rgb(255, 255, 255)​;​ z-index:​ 2147483647;​">​…​</div>​
(anonymous) @ critcss.snippet.js:31
findCriticalCSS @ critcss.snippet.js:32
(anonymous) @ critcss.snippet.js:15
critcss.snippet.js:39 Found 122 critical nodes
critcss.snippet.js:64 allowing CSSFontFaceRule {style: CSSStyleDeclaration, type: 5, cssText: "@font-face { font-family: "Libre Baskerville"; fon…-latin_latin-ext-regular.woff") format("woff"); }", parentRule: null, parentStyleSheet: CSSStyleSheet}
(anonymous) @ critcss.snippet.js:64
(anonymous) @ critcss.snippet.js:50
findCriticalCSS @ critcss.snippet.js:44
(anonymous) @ critcss.snippet.js:15
critcss.snippet.js:64 allowing CSSFontFaceRule {style: CSSStyleDeclaration, type: 5, cssText: "@font-face { font-family: "Libre Baskerville"; fon…e-v4-latin_latin-ext-700.woff") format("woff"); }", parentRule: null, parentStyleSheet: CSSStyleSheet}
(anonymous) @ critcss.snippet.js:64
(anonymous) @ critcss.snippet.js:50
findCriticalCSS @ critcss.snippet.js:44
(anonymous) @ critcss.snippet.js:15
critcss.snippet.js:64 allowing CSSFontFaceRule {style: CSSStyleDeclaration, type: 5, cssText: "@font-face { font-family: "Libre Baskerville"; fon…4-latin_latin-ext-italic.woff") format("woff"); }", parentRule: null, parentStyleSheet: CSSStyleSheet}
(anonymous) @ critcss.snippet.js:64
(anonymous) @ critcss.snippet.js:50
findCriticalCSS @ critcss.snippet.js:44
(anonymous) @ critcss.snippet.js:15
critcss.snippet.js:64 allowing CSSFontFaceRule {style: CSSStyleDeclaration, type: 5, cssText: "@font-face { font-family: "Didact Gothic"; font-st…t_greek_cyrillic-regular.woff") format("woff"); }", parentRule: null, parentStyleSheet: CSSStyleSheet}
(anonymous) @ critcss.snippet.js:64
(anonymous) @ critcss.snippet.js:50
findCriticalCSS @ critcss.snippet.js:44
(anonymous) @ critcss.snippet.js:15
critcss.snippet.js:68 Bad CSS rule undefined
(anonymous) @ critcss.snippet.js:68
(anonymous) @ critcss.snippet.js:50
findCriticalCSS @ critcss.snippet.js:44
(anonymous) @ critcss.snippet.js:15
critcss.snippet.js:69 Uncaught DOMException: Failed to execute 'matches' on 'Element': The provided selector is empty.
    at <anonymous>:55:83
    at Array.filter (<anonymous>)
    at <anonymous>:55:54
    at CSSRuleList.filter (<anonymous>)
    at <anonymous>:54:62
    at CSSRuleList.map (<anonymous>)
    at <anonymous>:50:44
    at StyleSheetList.map (<anonymous>)
    at findCriticalCSS (<anonymous>:44:38)
    at HTMLButtonElement.<anonymous> (<anonymous>:15:53)
(anonymous) @ critcss.snippet.js:69
(anonymous) @ critcss.snippet.js:50
findCriticalCSS @ critcss.snippet.js:44
(anonymous) @ critcss.snippet.js:15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment