Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save haalogen/760b6b9c96ad5cc9c0e1b809838c98fc to your computer and use it in GitHub Desktop.

Select an option

Save haalogen/760b6b9c96ad5cc9c0e1b809838c98fc to your computer and use it in GitHub Desktop.

Revisions

  1. @victor-homyakov victor-homyakov revised this gist Apr 20, 2020. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions detect-unused-css-selectors.js
    Original file line number Diff line number Diff line change
    @@ -37,13 +37,16 @@
    if (unused.length !== window.selectorStats.unused.length) {
    message.push(unused.length + ' unused');
    }
    window.selectorStats.unused = unused;
    if (added.length > 0) {
    message.push(added.length + ' added');
    window.selectorStats.added = added;
    }
    if (removed.length > 0) {
    message.push(removed.length + ' removed');
    message.push(removed.length + ' removed', removed);
    window.selectorStats.removed = removed;
    }
    window.selectorStats = { unused: unused, added: added, removed: removed };

    if (message.length > 0) {
    console.log('Selectors: ' + message.join(', '));
    }
  2. @victor-homyakov victor-homyakov revised this gist Apr 17, 2020. 1 changed file with 8 additions and 3 deletions.
    11 changes: 8 additions & 3 deletions detect-unused-css-selectors.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    /* eslint-disable no-var,no-console */
    // detect unused CSS selectors
    (function() {
    var parsedRules = parseCssRules();
    @@ -48,14 +49,14 @@
    }
    }, 1000);


    function parseCssRules() {
    var styleSheets = document.styleSheets,
    parsedRules = {
    fontFaces: [],
    keyframes: [],
    media: [],
    style: [],
    support: [],
    unknown: []
    };

    @@ -82,6 +83,9 @@
    parsedRules.style.push(rule.selectorText);
    // rule.cssText
    break;
    case 'CSSSupportsRule':
    parsedRules.support.push(rule.conditionText);
    break;
    default:
    parsedRules.unknown.push(rule);
    }
    @@ -94,7 +98,7 @@
    function detectDuplicateSelectors(parsedRules) {
    var seenSelectors = {},
    duplicatedSelectors = [],
    duplicatedSequence = []
    duplicatedSequence = [];

    parsedRules.style.forEach(function(selector) {
    if (selector in seenSelectors) {
    @@ -103,7 +107,7 @@
    } else {
    seenSelectors[selector] = true;
    if (duplicatedSequence.length > 5) {
    console.warn('Duplicated sequence of selectors:', duplicatedSequence)
    console.warn('Duplicated sequence of selectors:', duplicatedSequence);
    }
    duplicatedSequence = [];
    }
    @@ -119,6 +123,7 @@
    .filter(function(selector) {
    return !(
    selector === 'html' ||
    selector.includes(':hover') ||
    selector.includes('::after') ||
    selector.includes('::before')
    );
  3. @victor-homyakov victor-homyakov revised this gist Feb 18, 2018. 1 changed file with 53 additions and 24 deletions.
    77 changes: 53 additions & 24 deletions detect-unused-css-selectors.js
    Original file line number Diff line number Diff line change
    @@ -4,33 +4,47 @@
    console.log('Parsed CSS rules:', parsedRules);
    detectDuplicateSelectors(parsedRules);

    var selectorsToTrack = parsedRules.style
    .filter(function(selector) {
    return !(
    selector === 'html' ||
    selector.includes('::after') ||
    selector.includes('::before')
    );
    })
    .reduce(function(selectors, selector) {
    selectors[selector] = 0;
    return selectors;
    }, {});
    var selectorsToTrack = getSelectorsToTrack(parsedRules);
    window.selectorStats = { unused: [], added: [], removed: [] };
    console.log('Tracking style usage (inspect window.selectorStats for details)...');

    window.unusedSelectors = [];
    setInterval(function() {
    unusedSelectors = [];
    Object.keys(selectorsToTrack).forEach(function(selector) {
    if (document.querySelector(selector)) {
    selectorsToTrack[selector]++;
    }
    if (selectorsToTrack[selector] === 0) {
    unusedSelectors.push(selector);
    }
    });
    var newSelectors = getSelectorsToTrack(parseCssRules());

    // Calculation order for removed/added/unused is significant
    var removed = Object.keys(selectorsToTrack)
    .filter(selector => newSelectors[selector] === undefined);

    if (unusedSelectors.length > 0) {
    console.log(unusedSelectors.length + ' unused selectors (inspect window.unusedSelectors for details)');
    var added = Object.keys(newSelectors)
    .filter(selector => {
    if (selectorsToTrack[selector] === undefined) {
    selectorsToTrack[selector] = 0;
    return true;
    }
    return false;
    });

    var unused = Object.keys(selectorsToTrack)
    .filter(selector => {
    if (document.querySelector(selector)) {
    selectorsToTrack[selector]++;
    }
    return selectorsToTrack[selector] === 0;
    });

    var message = [];
    if (unused.length !== window.selectorStats.unused.length) {
    message.push(unused.length + ' unused');
    }
    if (added.length > 0) {
    message.push(added.length + ' added');
    }
    if (removed.length > 0) {
    message.push(removed.length + ' removed');
    }
    window.selectorStats = { unused: unused, added: added, removed: removed };
    if (message.length > 0) {
    console.log('Selectors: ' + message.join(', '));
    }
    }, 1000);

    @@ -99,4 +113,19 @@
    console.log('List of all duplicated selectors:', duplicatedSelectors);
    }
    }

    function getSelectorsToTrack(parsedRules) {
    return parsedRules.style
    .filter(function(selector) {
    return !(
    selector === 'html' ||
    selector.includes('::after') ||
    selector.includes('::before')
    );
    })
    .reduce(function(selectors, selector) {
    selectors[selector] = 0;
    return selectors;
    }, {});
    }
    }());
  4. @victor-homyakov victor-homyakov revised this gist Feb 16, 2018. 1 changed file with 81 additions and 82 deletions.
    163 changes: 81 additions & 82 deletions detect-unused-css-selectors.js
    Original file line number Diff line number Diff line change
    @@ -1,103 +1,102 @@
    // detect unused CSS selectors
    (function() {
    var styleSheets = document.styleSheets,
    parsedRules = {
    fontFaces: [],
    keyframes: [],
    media: [],
    style: [],
    unknown: []
    };
    var parsedRules = parseCssRules();
    console.log('Parsed CSS rules:', parsedRules);
    detectDuplicateSelectors(parsedRules);

    for (var i = 0; i < styleSheets.length; i++) {
    var styleSheet = styleSheets[i];
    var rules = styleSheet.cssRules; // styleSheet.rules
    for (var j = 0; j < rules.length; j++) {
    var rule = rules[j];
    var ruleClass = Object.prototype.toString.call(rule).replace(/\[object (.+)]/, '$1');
    var selectorsToTrack = parsedRules.style
    .filter(function(selector) {
    return !(
    selector === 'html' ||
    selector.includes('::after') ||
    selector.includes('::before')
    );
    })
    .reduce(function(selectors, selector) {
    selectors[selector] = 0;
    return selectors;
    }, {});

    switch (ruleClass) {
    case 'CSSFontFaceRule':
    parsedRules.fontFaces.push(rule.cssText);
    break;
    case 'CSSKeyframesRule':
    parsedRules.keyframes.push(rule.cssText);
    break;
    case 'CSSMediaRule':
    // if (rule.conditionText)
    parsedRules.media.push(rule.conditionText);
    break;
    case 'CSSStyleRule':
    // if (rule.selectorText)
    parsedRules.style.push(rule.selectorText);
    // rule.cssText
    break;
    default:
    parsedRules.unknown.push(rule);
    window.unusedSelectors = [];
    setInterval(function() {
    unusedSelectors = [];
    Object.keys(selectorsToTrack).forEach(function(selector) {
    if (document.querySelector(selector)) {
    selectorsToTrack[selector]++;
    }

    /*
    if (rule.selectorText) {
    parsedRules.style.push(rule.selectorText);
    // if (document.querySelector(rule.selectorText)) {
    // parsedRules.style.push(rule.selectorText);
    // } else {
    // parsedRules.unusedStyle.push(rule.selectorText);
    // }
    } else if (rule.conditionText) {
    parsedRules.media.push(rule.conditionText);
    } else if (rule instanceof window.CSSFontFaceRule) {
    parsedRules.fontFaces.push(rule.cssText);
    } else if (rule instanceof window.CSSKeyframesRule) {
    parsedRules.keyframes.push(rule.cssText);
    } else {
    parsedRules.unknown.push(rule);
    if (selectorsToTrack[selector] === 0) {
    unusedSelectors.push(selector);
    }
    */
    });

    if (unusedSelectors.length > 0) {
    console.log(unusedSelectors.length + ' unused selectors (inspect window.unusedSelectors for details)');
    }
    }
    }, 1000);

    console.log('Parsed CSS rules:', parsedRules);

    window.unusedSelectors = [];
    function parseCssRules() {
    var styleSheets = document.styleSheets,
    parsedRules = {
    fontFaces: [],
    keyframes: [],
    media: [],
    style: [],
    unknown: []
    };

    var selectors = {},
    duplicatedSelectors = [],
    duplicatedSequence = [];
    for (var i = 0; i < styleSheets.length; i++) {
    var styleSheet = styleSheets[i];
    var rules = styleSheet.cssRules; // styleSheet.rules
    for (var j = 0; j < rules.length; j++) {
    var rule = rules[j];
    var ruleClass = Object.prototype.toString.call(rule).replace(/\[object (.+)]/, '$1');

    parsedRules.style.forEach(function(selector) {
    if (selector in selectors) {
    duplicatedSelectors.push(selector);
    duplicatedSequence.push(selector);
    } else {
    selectors[selector] = 0;
    if (duplicatedSequence.length > 5) {
    console.warn('Duplicated sequence of selectors:', duplicatedSequence)
    switch (ruleClass) {
    case 'CSSFontFaceRule':
    parsedRules.fontFaces.push(rule.cssText);
    break;
    case 'CSSKeyframesRule':
    parsedRules.keyframes.push(rule.cssText);
    break;
    case 'CSSMediaRule':
    // if (rule.conditionText)
    parsedRules.media.push(rule.conditionText);
    break;
    case 'CSSStyleRule':
    // if (rule.selectorText)
    parsedRules.style.push(rule.selectorText);
    // rule.cssText
    break;
    default:
    parsedRules.unknown.push(rule);
    }
    }
    duplicatedSequence = [];
    }
    });

    if (duplicatedSelectors.length > 0) {
    console.log('List of all duplicated selectors:', duplicatedSelectors);
    return parsedRules;
    }

    setInterval(function() {
    unusedSelectors = [];
    Object.keys(selectors).forEach(function(selector) {
    if (selector === 'html' ||
    selector.includes('::after') ||
    selector.includes('::before') ||
    document.querySelector(selector)) {
    selectors[selector]++;
    }
    if (!selectors[selector]) {
    unusedSelectors.push(selector);
    function detectDuplicateSelectors(parsedRules) {
    var seenSelectors = {},
    duplicatedSelectors = [],
    duplicatedSequence = []

    parsedRules.style.forEach(function(selector) {
    if (selector in seenSelectors) {
    duplicatedSelectors.push(selector);
    duplicatedSequence.push(selector);
    } else {
    seenSelectors[selector] = true;
    if (duplicatedSequence.length > 5) {
    console.warn('Duplicated sequence of selectors:', duplicatedSequence)
    }
    duplicatedSequence = [];
    }
    });

    if (unusedSelectors.length > 0) {
    console.log(unusedSelectors.length + ' unused selectors');
    if (duplicatedSelectors.length > 0) {
    console.log('List of all duplicated selectors:', duplicatedSelectors);
    }
    }, 1000);
    }
    }());
  5. @victor-homyakov victor-homyakov created this gist Feb 15, 2018.
    103 changes: 103 additions & 0 deletions detect-unused-css-selectors.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,103 @@
    // detect unused CSS selectors
    (function() {
    var styleSheets = document.styleSheets,
    parsedRules = {
    fontFaces: [],
    keyframes: [],
    media: [],
    style: [],
    unknown: []
    };

    for (var i = 0; i < styleSheets.length; i++) {
    var styleSheet = styleSheets[i];
    var rules = styleSheet.cssRules; // styleSheet.rules
    for (var j = 0; j < rules.length; j++) {
    var rule = rules[j];
    var ruleClass = Object.prototype.toString.call(rule).replace(/\[object (.+)]/, '$1');

    switch (ruleClass) {
    case 'CSSFontFaceRule':
    parsedRules.fontFaces.push(rule.cssText);
    break;
    case 'CSSKeyframesRule':
    parsedRules.keyframes.push(rule.cssText);
    break;
    case 'CSSMediaRule':
    // if (rule.conditionText)
    parsedRules.media.push(rule.conditionText);
    break;
    case 'CSSStyleRule':
    // if (rule.selectorText)
    parsedRules.style.push(rule.selectorText);
    // rule.cssText
    break;
    default:
    parsedRules.unknown.push(rule);
    }

    /*
    if (rule.selectorText) {
    parsedRules.style.push(rule.selectorText);
    // if (document.querySelector(rule.selectorText)) {
    // parsedRules.style.push(rule.selectorText);
    // } else {
    // parsedRules.unusedStyle.push(rule.selectorText);
    // }
    } else if (rule.conditionText) {
    parsedRules.media.push(rule.conditionText);
    } else if (rule instanceof window.CSSFontFaceRule) {
    parsedRules.fontFaces.push(rule.cssText);
    } else if (rule instanceof window.CSSKeyframesRule) {
    parsedRules.keyframes.push(rule.cssText);
    } else {
    parsedRules.unknown.push(rule);
    }
    */
    }
    }

    console.log('Parsed CSS rules:', parsedRules);

    window.unusedSelectors = [];

    var selectors = {},
    duplicatedSelectors = [],
    duplicatedSequence = [];

    parsedRules.style.forEach(function(selector) {
    if (selector in selectors) {
    duplicatedSelectors.push(selector);
    duplicatedSequence.push(selector);
    } else {
    selectors[selector] = 0;
    if (duplicatedSequence.length > 5) {
    console.warn('Duplicated sequence of selectors:', duplicatedSequence)
    }
    duplicatedSequence = [];
    }
    });

    if (duplicatedSelectors.length > 0) {
    console.log('List of all duplicated selectors:', duplicatedSelectors);
    }

    setInterval(function() {
    unusedSelectors = [];
    Object.keys(selectors).forEach(function(selector) {
    if (selector === 'html' ||
    selector.includes('::after') ||
    selector.includes('::before') ||
    document.querySelector(selector)) {
    selectors[selector]++;
    }
    if (!selectors[selector]) {
    unusedSelectors.push(selector);
    }
    });

    if (unusedSelectors.length > 0) {
    console.log(unusedSelectors.length + ' unused selectors');
    }
    }, 1000);
    }());