Skip to content

Instantly share code, notes, and snippets.

@pibby
Forked from james-Ballyhoo/critcss.snippet.js
Last active August 23, 2020 16:22
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save pibby/ded331e8366735b0415d to your computer and use it in GitHub Desktop.
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.
/* 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