-
-
Save pibby/ded331e8366735b0415d to your computer and use it in GitHub Desktop.
Critical CSS Finder w/media query support for those of us who write mobile-first CSS; will output to console; configurable viewport height on line 12.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Critical CSS Finder w/media query support and output to console | |
by Katie Harron - https://github.com/pibby - https://pibby.com | |
forked from james-Ballyhoo (https://gist.github.com/james-Ballyhoo/04761ed2a5778c505527) who forked from PaulKinlan (https://gist.github.com/PaulKinlan/6284142) | |
I don't know why this isn't keeping my 2 space indents :( | |
*/ | |
(function() { | |
function findCriticalCSS(w, d) { | |
// Pseudo classes formatting | |
var formatPseudo = /([^\s,\:\(])\:\:?(?!not)[a-zA-Z\-]{1,}(?:\(.*?\))?/g; | |
// Height in px we want critical styles for | |
var targetHeight = 900; | |
var criticalNodes = []; | |
// Step through the document tree and identify nodes that are within our targetHeight | |
var walker = d.createTreeWalker(d, NodeFilter.SHOW_ELEMENT, function(node) { return NodeFilter.FILTER_ACCEPT; }, true); | |
while(walker.nextNode()) { | |
var node = walker.currentNode; | |
var rect = node.getBoundingClientRect(); | |
if (rect.top < targetHeight) { | |
criticalNodes.push(node); | |
} | |
} | |
console.log("Found " + criticalNodes.length + " critical nodes."); | |
// Find stylesheets that have been loaded | |
var stylesheets = document.styleSheets; | |
console.log("Found " + stylesheets.length + " stylesheet(s)."); | |
var outputCss = Array.prototype.map.call(stylesheets,function(sheet) { | |
var rules = sheet.rules || sheet.cssRules; | |
// If style rules are present | |
if (rules) { | |
return { | |
sheet: sheet, | |
// Convert rules into an array | |
rules: Array.prototype.map.call(rules, function(rule) { | |
try { | |
// If the rule contains a media query | |
if (rule instanceof CSSMediaRule) { | |
var nestedRules = rule.rules || rule.cssRules; | |
var css = Array.prototype.filter.call(nestedRules, function(rule) { | |
return criticalNodes.filter(function(e){ return e.matches(rule.selectorText.replace(formatPseudo, "$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) { // If rule does not contain a media query | |
return criticalNodes.filter(function(e) { return e.matches(rule.selectorText.replace(formatPseudo, "$1")) }).length > 0 ? rule.cssText : null; | |
} else { // If identified via CSSRule like @font and @keyframes | |
return rule.cssText; | |
} | |
} catch(e) { | |
/* This results in an error if you have print styles with @page embedded. As I do, I'm commenting it out. */ | |
/*console.error("Improper CSS rule ", rule.selectorText); | |
throw e;*/ | |
} | |
}).filter(function(e) { return e; }) | |
} | |
} else { | |
return null; | |
} | |
}).filter(function(cssEntry) { return cssEntry && cssEntry.rules.length > 0 }) | |
.map(function(cssEntry) { return cssEntry.rules.join(""); }) | |
.reduce(function(css, out) {return out + css}, "") | |
// Remove linebreaks | |
console.log(outputCss.replace(/\n/g,"")) | |
} | |
findCriticalCSS(window, document); | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment