Skip to content

Instantly share code, notes, and snippets.

@vagelisp
Forked from pibby/critcss.snippet.js
Created February 15, 2017 13:32
Show Gist options
  • Save vagelisp/11d66dd1cfc96b1d20ed532fbdce0646 to your computer and use it in GitHub Desktop.
Save vagelisp/11d66dd1cfc96b1d20ed532fbdce0646 to your computer and use it in GitHub Desktop.

Revisions

  1. @pibby pibby revised this gist Nov 7, 2015. No changes.
  2. @pibby pibby revised this gist Oct 26, 2015. No changes.
  3. @pibby pibby revised this gist Oct 25, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion critcss.snippet.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    /* 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
    I don't know why this isn't keeping my 2 space indents :(
    */

    (function() {
  4. @pibby pibby revised this gist Oct 25, 2015. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion critcss.snippet.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    /* 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) */
    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) {
  5. @pibby pibby revised this gist Oct 25, 2015. 1 changed file with 61 additions and 81 deletions.
    142 changes: 61 additions & 81 deletions critcss.snippet.js
    Original file line number Diff line number Diff line change
    @@ -1,91 +1,71 @@
    (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);
    /* 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) */

    container.querySelector("#CRIT_FIND").addEventListener("click",function(){
    container.querySelector("textarea").value = findCriticalCSS(window,document);
    });
    (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 = [];

    container.querySelector("#CRIT_CLOSE").addEventListener("click",function(){
    container.remove();
    container = null;
    });
    // 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);

    function findCriticalCSS(w, d){
    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.");

    //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 = [];
    // Find stylesheets that have been loaded
    var stylesheets = document.styleSheets;
    console.log("Found " + stylesheets.length + " stylesheet(s).");

    //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");
    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;

    //Grab loaded stylesheets
    var sheets = document.styleSheets;
    } 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. */

    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;
    /*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}, "")

    }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) + "\"";
    });

    }
    // Remove linebreaks
    console.log(outputCss.replace(/\n/g,""))
    }

    findCriticalCSS(window, document);
    })()
  6. James created this gist Sep 18, 2015.
    91 changes: 91 additions & 0 deletions critcss.snippet.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,91 @@
    (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) + "\"";
    });

    }

    })()