gog
gog

Reputation: 11347

Get the computed style and omit defaults

I'm trying to get the current runtime style of an element and filter out properties that have default values. For example, with markup like this:

<style>
    .foo { background: red }
    span { font-size:30px }
</style>


<div style="color: blue">
    <span id="bar" class="foo">hello</span>
</div>

I'd like the result to be:

 background-color: red;
 color: blue;
 font-size: 30px;

I tried window.getComputedStyle, but it returns a lot of stuff and I'm unsure how to filter out defaults. Any pointers will be appreciated.

Upvotes: 15

Views: 6446

Answers (7)

JOHNNY SHAHIN
JOHNNY SHAHIN

Reputation: 23

try this one on for size:

  1. Place these somewhere.

    <textarea id="t1"> </textarea> <textarea id="t2"> </textarea> <textarea id="t3"> </textarea> <textarea id="t4"> </textarea>

  2. next

function test() {
    let arr = [];
    let elem = _("right-Sidebar");
    let cStyl = window.getComputedStyle(elem);
    let txt1;

    for (x of cStyl) { txt1 += x + "=" + cStyl[x] + "\n"; }
    _("t1").value = txt1;
    let ss = document.getElementsByTagName("Style");
    const sstext = ss[0].innerHTML;
    ss[0].innerHTML = "";

    let elem2 = _("right-Sidebar");
    let cStyl2 = window.getComputedStyle(elem2);
    let txt2;

    for (x2 of cStyl2) { txt2 += x2 + "=" + cStyl2[x2] + "\n"; }
    _("t2").value = txt2;

    let div = _el(elem.tagName);
    let dfrag = new DocumentFragment();
    dfrag.appendChild(div);
    document.body.appendChild(dfrag);

    let cStyl3 = window.getComputedStyle(div);
    let txt3;

    for (x3 of cStyl3) { txt3 += x3 + "=" + cStyl3[x3] + "\n"; }
    _("t3").value = txt3;

    alert(_("t3").value === _("t2").value);


    let val1 = _("t2").value.split("\n");
    let val2 = _("t1").value.split("\n");


    let i = 0;
    while (i < val1.length) {

        if (val1[i] !== val2[i]) {
            arr.push(val1[i] + "\n" + val2[i] + "\n\n\n");
        }
        i++;
    }
    _("t4").value = "";
    _("t4").value = arr.join("\n");
}

Upvotes: 0

&#201;mile Perron
&#201;mile Perron

Reputation: 883

I have built what I believe to be a more modern, complete and efficient solution, based on mattsven's answer.

All you have to do is call the getUserStyles method with the node as a parameter, as such: Styles.getUserStyles(document.querySelector('#bar'))

Obviously, this snippet was not made with older browser support in mind, so some adjustments should be made if you are to use this on a public website.

class Styles {
    // Returns a dummy iframe with no styles or content
    // This allows us to get default styles from the browser for an element
    static getStylesIframe() {
        if (typeof window.blankIframe != 'undefined') {
            return window.blankIframe;
        }

        window.blankIframe = document.createElement('iframe');
        document.body.appendChild(window.blankIframe);

        return window.blankIframe;
    }

    // Turns a CSSStyleDeclaration into a regular object, as all values become "" after a node is removed
    static getStylesObject(node, parentWindow) {
        const styles = parentWindow.getComputedStyle(node);
        let stylesObject = {};

        for (let i = 0; i < styles.length; i++) {
            const property = styles[i];
            stylesObject[property] = styles[property];
        }

        return stylesObject;
    }

    // Returns a styles object with the browser's default styles for the provided node
    static getDefaultStyles(node) {
        const iframe = Styles.getStylesIframe();
        const iframeDocument = iframe.contentDocument;
        const targetElement = iframeDocument.createElement(node.tagName);

        iframeDocument.body.appendChild(targetElement);
        const defaultStyles = Styles.getStylesObject(targetElement, iframe.contentWindow);

        targetElement.remove();

        return defaultStyles;
    }

    // Returns a styles object with only the styles applied by the user's CSS that differ from the browser's default styles
    static getUserStyles(node) {
        const defaultStyles = Styles.getDefaultStyles(node);
        const styles = Styles.getStylesObject(node, window);
        let userStyles = {};

        for (let property in defaultStyles) {
            if (styles[property] != defaultStyles[property]) {
                userStyles[property] = styles[property];
            }
        }

        return userStyles;
    }
};

Upvotes: 3

Taras Galatsiuk
Taras Galatsiuk

Reputation: 1

Here's an option if you need to use getPropertyValue afterwards, like here

var computedStyles = getComputedStyles(document.body);
var propertyValue = computedStyles.getPropertyValue("prop-name");

Use this function:

function getDefaultComputedStyles(el)
{
    var temp = document.createElement("div");
    document.body.appendChild(temp);
    var defaultStyles = getComputedStyle(temp);
    var extraStyles = getComputedStyle(el);
    var foundStyles = [];

    for(var i=0; i<defaultStyles.length; i++)
    {
        var extraStyleIndex = extraStyles[i];
        var extraStyleValue = extraStyles.getPropertyValue(extraStyles[i]);
        if(defaultStyles.getPropertyValue(defaultStyles[i]) !== extraStyleValue)
        {
          foundStyles.push(JSON.parse(`{"${extraStyleIndex}":"${extraStyleValue}"}`));
        }
    }
    foundStyles.getPropertyValue = function(ind){
        var result = this.filter(el => (`${ind}` in el));
        return result[0]!=undefined ? result[0][Object.keys(result[0])] : null;
    }

    return foundStyles;
}

Upvotes: 0

Anas Nakawa
Anas Nakawa

Reputation: 2015

there you go, i did this by adding a new dummy DOM element, to know which styles are default for any element.

/**
 * IE does not have `getComputedStyle` 
 */

window.getComputedStyle = window.getComputedStyle || function( element ) {
  return element.currentStyle;
}

/**
 * get computed style for an element, excluding any default styles
 *
 * @param {DOM} element
 * @return {object} difference
 */

function getStylesWithoutDefaults( element ) {

  // creating an empty dummy object to compare with
  var dummy = document.createElement( 'element-' + ( new Date().getTime() ) );
  document.body.appendChild( dummy );

  // getting computed styles for both elements
  var defaultStyles = getComputedStyle( dummy );
  var elementStyles = getComputedStyle( element );

  // calculating the difference
  var diff = {};
  for( var key in elementStyles ) {
    if(elementStyles.hasOwnProperty(key)
          && defaultStyles[ key ] !== elementStyles[ key ] )
    {
      diff[ key ] = elementStyles[ key ];
    }
  }

  // clear dom
  dummy.remove();

  return diff;
}


/**
 * usage
 */

console.log( getStylesWithoutDefaults( document.getElementById( 'bar' ) ) );

Notes:

  • the result will have some extra properties, not only those you've mentioned.

demo - console should be opened

Upvotes: 16

mattsven
mattsven

Reputation: 23253

Here's a more robust solution to this, using an iframe. This solution is inefficient for more than one element at a time, in which case you'll want to use a fragment to batch element insertion and pass in an array of tag names.

var getDefaultStyling = function(tagName){
    if(!tagName) tagName = "dummy-tag-name";

    //  Create dummy iframe

    var iframe = document.createElement("iframe");

    document.body.appendChild(iframe);

    //  Create element within the iframe's document

    var iframeDocument = iframe.contentDocument;
    var targetElement = iframeDocument.createElement(tagName);

    iframeDocument.body.appendChild(targetElement);

    //  Grab styling (CSSStyleDeclaration is live, and all values become "" after element removal)

    var styling = iframe.contentWindow.getComputedStyle(targetElement);
    var clonedStyling = {};

    for(var i = 0, len = styling.length; i < len; i++){
        var property = styling[i];

        clonedStyling[i] = property;
        clonedStyling[property] = styling[property];
    }

    //  Remove iframe

    document.body.removeChild(iframe);

    //  Return cloned styling

    return clonedStyling;
};

var getUniqueUserStyling = function(element){
    var allStyling = window.getComputedStyle(element);
    var defaultStyling = getDefaultStyling(element.tagName);

    var userStyling = {};

    for(var i = 0, len = allStyling.length; i < len; i++){
        var property = allStyling[i];
        var value = allStyling[property];
        var defaultValue = defaultStyling[property];

        if(value != defaultValue){
            userStyling[property] = value;
        }
    }

    return userStyling;
};

Usage: getUniqueUserStyling(myElement).

Upvotes: 3

Karim AG
Karim AG

Reputation: 2193

Here it is .. using pure javscript .. I only added jquery to the fiddle to set document.ready event.

Here is the code:

$(document).ready(function () {
    var div = document.getElementById('output');
    var x = document.getElementById('frame').contentWindow.document.createElement('x');
    document.getElementById('frame').contentWindow.document.body.appendChild(x);
    var defautlStyles = window.getComputedStyle(x);
    var barStyles = window.getComputedStyle(document.getElementById('bar'));
    for (i = 0; i < defautlStyles.length; i++) {
        if (defautlStyles["" + defautlStyles[i]] != barStyles["" + barStyles[i]]) {
            var p = document.createElement('p');
            p.innerText += barStyles[i] + ": " + barStyles["" + barStyles[i]];
            div.appendChild(p);
        }
    }
});

I used an iframe to add an element to it so the style of the added element won't be affected by the document default styles. And here is the FIDDLE

Hope it helps...

Upvotes: -1

SW4
SW4

Reputation: 71140

You can use, e.g:

window.getComputedStyle(document.getElementById('bar'))

This will return an object containing all the computed styles for the element with the id 'bar'.

So, for example you could do:

var styles = window.getComputedStyle(document.getElementById('bar'));
styles = "background-color:" + styles['background-color'] + ";color:" + styles['color'] + ";font-size:" + styles['font-size'] + ";";
console.log(styles);

Note that color values will be returned in RGBA format.

Demo Fiddle

Upvotes: -2

Related Questions