benjamincharity
1/12/2017 - 4:23 PM

List unique CSS properties for all DOM elements

List unique CSS properties for all DOM elements

/**
 * List unique CSS properties for all DOM elements
 * Initially created to list unique font stacks on a page
 * @see {@link http://stackoverflow.com/a/35022690/ Inspired by this StackOverflow answer}
 *
 * @see {@link https://gist.github.com/macbookandrew/f33dbbc0aa582d0515919dc5fb95c00a/ URL for this file}
 *
 * @author AndrewRMinion Design (https://andrewrminion.com)
 * @version 1.1
 *
 * @license MIT
 * @copyright AndrewRMinion Design
 *
 * @example Show all unique font stacks in use on the current page
 *          console.log(styleInPage('fontFamily'));
 *
 * @example Show a list of all DOM elements with their computed font stack
 *          console.log(styleInPage('fontFamily', true));
 *
 * @example Highlight all DOM elements using a particular font stack (pass in the array key from styleInPage)
 *          var fontStacksInUse = styleInPage('fontFamily');
 *          console.log(fontStacksInUse);
 *          highlightInPage(8);
 */

/**
 * Get an array of elements with their computed styles
 * @param   {string}  css     CSS property, camelCased for JS
 * @param   {boolean} verbose whether to output an array of all elements with their style properties
 * @returns {array}   array of unique properties, or if verbose is true, array of all elements with their properties
 */
function styleInPage(css, verbose) {
    // polyfill getComputedStyle
    if (typeof getComputedStyle == "undefined") {
        getComputedStyle = function (elem) {
            return elem.currentStyle;
        }
    }

    // set vars
    var style,
        thisNode,
        styleId,
        allStyles = [],
        nodes = document.body.getElementsByTagName('*');

    // loop over all elements
    for (var i = 0; i < nodes.length; i++) {
        thisNode = nodes[i];
        if (thisNode.style) {
            styleId = '#' + (thisNode.id || thisNode.nodeName + '(' + i + ')');
            style = thisNode.style.fontFamily || getComputedStyle(thisNode, '')[css];

            // get element’s style
            if (style) {
                if (verbose) {
                    allStyles.push([styleId, style]);
                } else if (allStyles.indexOf(style) == -1) {
                    allStyles.push(style);
                }

                // add data-attribute with key for allStyles array
                thisNode.dataset.styleId = allStyles.indexOf(style);
            }

            // get element:before’s style
            styleBefore = getComputedStyle(thisNode, ':before')[css];
            if (styleBefore) {
                if (verbose) {
                    allStyles.push([styleId, styleBefore]);
                } else if (allStyles.indexOf(styleBefore) == -1) {
                    allStyles.push(styleBefore);
                }

                // add data-attribute with key for allStyles array
                thisNode.dataset.styleId = allStyles.indexOf(styleBefore);
            }

            // get element:after’s style
            styleAfter = getComputedStyle(thisNode, ':after')[css];
            if (styleAfter) {
                if (verbose) {
                    allStyles.push([styleId, styleAfter]);
                } else if (allStyles.indexOf(styleAfter) == -1) {
                    allStyles.push(styleAfter);
                }

                // add data-attribute with key for allStyles array
                thisNode.dataset.styleId = allStyles.indexOf(styleAfter);
            }
        }
    }
    return allStyles;
}

/**
 * Highlight elements with the specified style
 * @param {integer} styleId data-style-id to highlight
 */
function highlightInPage(styleId) {
    var thisNode,
        allNodes = document.body.getElementsByTagName('*'),
        nodes = document.body.querySelectorAll('[data-style-id="' + styleId + '"]');

    // remove previous highlights
    for (var i = 0; i < allNodes.length; i++) {
        allNodes[i].classList.remove('style-highlight');
    }

    // add highlight to specified nodes
    for (var i = 0; i < nodes.length; i++) {
        thisNode = nodes[i];
        thisNode.classList.add('style-highlight');
    }
}

/**
 * Clear all highlights
 */
function clearHighlights() {
    highlightInPage();
}

/**
 * Add blank stylesheet for highlight rule
 * @returns {CSSStyleSheet} new blankstylesheet
 */
var sheet = (function() {
    // Create the <style> tag
    var style = document.createElement("style");

    // WebKit hack :(
    style.appendChild(document.createTextNode(""));

    // Add the <style> element to the page
    document.head.appendChild(style);

    return style.sheet;
})();

/**
 * Add specified CSS rule to the specified stylesheet
 * @param {CSSStyleSheet} sheet    a CSSStyleSheet object
 * @param {string}        selector CSS selector rule; include “.” for classes or “#” for IDs
 * @param {string}        rules    CSS properties for this selector
 * @param {integer}       index    index detailing where to add the new rule
 */
function addCSSRule(sheet, selector, rules, index = 0) {
    if ("insertRule" in sheet) {
        sheet.insertRule(selector + "{" + rules + "}", index);
    } else if ("addRule" in sheet) {
        sheet.addRule(selector, rules, index);
    }
}

// add a yellow background to highlighted elements
addCSSRule(sheet, ".style-highlight", "background-color: yellow");