Reputation: 11347
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
Reputation: 23
try this one on for size:
Place these somewhere.
<textarea id="t1"> </textarea>
<textarea id="t2"> </textarea>
<textarea id="t3"> </textarea>
<textarea id="t4"> </textarea>
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
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
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
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:
demo - console should be opened
Upvotes: 16
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
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
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.
Upvotes: -2