308

Is there a way in jQuery to get all CSS from an existing element and apply it to another without listing them all?

I know it would work if they were a style attribute with attr(), but all of my styles are in an external style sheet.

yoozer8
  • 7,361
  • 7
  • 58
  • 93
alex
  • 479,566
  • 201
  • 878
  • 984

5 Answers5

345

A couple years late, but here is a solution that retrieves both inline styling and external styling:

function css(a) {
    var sheets = document.styleSheets, o = {};
    for (var i in sheets) {
        var rules = sheets[i].rules || sheets[i].cssRules;
        for (var r in rules) {
            if (a.is(rules[r].selectorText)) {
                o = $.extend(o, css2json(rules[r].style), css2json(a.attr('style')));
            }
        }
    }
    return o;
}

function css2json(css) {
    var s = {};
    if (!css) return s;
    if (css instanceof CSSStyleDeclaration) {
        for (var i in css) {
            if ((css[i]).toLowerCase) {
                s[(css[i]).toLowerCase()] = (css[css[i]]);
            }
        }
    } else if (typeof css == "string") {
        css = css.split("; ");
        for (var i in css) {
            var l = css[i].split(": ");
            s[l[0].toLowerCase()] = (l[1]);
        }
    }
    return s;
}

Pass a jQuery object into css() and it will return an object, which you can then plug back into jQuery's $().css(), ex:

var style = css($("#elementToGetAllCSS"));
$("#elementToPutStyleInto").css(style);

:)

George G
  • 7,443
  • 12
  • 45
  • 59
marknadal
  • 7,534
  • 4
  • 25
  • 22
  • 15
    BTW, when you say a *JSON object*, you just mean a JavaScript object right? – alex Apr 29 '11 at 10:11
  • 1
    +1 Mark, great solution which i've just given credit to in [this answer](http://stackoverflow.com/questions/6858042/how-to-tell-if-a-div-is-height-constrained/6858194#6858194) :) – Town Jul 28 '11 at 11:46
  • 3
    this looks awesome, but when I'm trying it it misses out on certain properties such as font-family. – Damon Aug 18 '11 at 19:56
  • @Damon any clue why? Is it a per-browser issue? – marknadal Aug 22 '11 at 08:00
  • It's fine actually. I was erroneously expecting inline styles to come with it. – Damon Aug 22 '11 at 13:30
  • 3
    @Damon: That's a valid assumption, considering the first line of the answer says *...here is a solution that retrieves both **inline styling** and external styling*. – alex Nov 23 '11 at 23:44
  • @Damon it does not miss out on certain properties, inline or not. I just checked on it myself. Could you please provide jsfiddle or gist showing that it does not work? Elsewise it worked just fine. – marknadal Nov 26 '11 at 13:25
  • 1
    My bad. When I said inline I was really talking about inherited styles – Damon Nov 28 '11 at 23:55
  • 1
    One problem with otherwise great solution is some browsers apply Same Origin Policy and will set `cssRules` to `null`. – alex Jan 19 '12 at 06:16
  • +1, but it seems vulnerable to clobbering styles with higher precedence if they're in the wrong order in a stylesheet. Solving that may be quite complex and probably expensive. – eyelidlessness Aug 28 '12 at 23:32
  • Just tried using the code, it seemed to trip up giving me this error in chrome: Uncaught Error: Syntax error, unrecognized expression: button.ui-button::-moz-focus-inner – Ally Aug 30 '12 at 11:31
  • "button.ui" does not appear anywhere in this code, so I'm not sure how it could have a syntax error on that? Are you sure it isn't a browser plugin? I can't get the error to happen. Have a JSFiddle? – marknadal Aug 31 '12 at 21:54
  • 7
    This code doesn't work any more (always returns empty object in Chrome) – Ivan Castellanos Feb 26 '13 at 10:40
  • Providing a patch/fix or jsFiddle that highlights exactly where your error is happening (if any) would be greatly appreciated - to back up your claim and provide insight for others. Thanks! – marknadal Feb 27 '13 at 07:47
  • Note: getMatchedCSSRules is currently only implemented in Webkit browsers ( [Compatibility Table](http://compatibility.shwups-cms.ch/en/home/?&property=getMatchedCSSRules) ), so using this in Firefox or IE will cause problems. You'll need a polyfill to handle this code. – Brandon Belvin Jul 02 '13 at 18:29
  • 13
    Note: Moderators have modified my original code, I give no guarantee anything will work. – marknadal Jul 03 '13 at 23:05
  • 1
    does this also get css for all descendant elements? – Eric Hartford Dec 26 '13 at 23:14
  • Note, this does not retrieve the browser default styles, only styles that have been applied by stylesheets or inline. – Mike Jun 28 '14 at 18:16
  • 1
    Not working for me , I tested in chrome console. it is not working. When i put "var style = css(jQuery('#main input'));" after executing the css() function, i got following error "Uncaught Syntax error, unrecognized expression: hover " – Mohamed Hussain Nov 05 '14 at 09:05
  • CORS is starting to piss me off with all it's retardedness: You'll get a `Error: The operation is insecure.` if the CSS is from remote site... – Tomáš Zato Dec 03 '14 at 05:44
  • 2
    here is a fiddle of my version, which is quite similar, but doesn't choke on psuedo-selectors. also has a demo: http://jsfiddle.net/a2f0phg5/1/ – chiliNUT Feb 14 '15 at 00:10
  • I found this solution craps out if you use @import, any way around that, other than of course not using @import? – Sam Luther Jul 31 '15 at 17:06
  • This won't work for HTTPS sites in Chrome due to security policy – Josh Hibschman Feb 17 '16 at 22:26
  • Joining @Mike I want to remind that the solutions above do not copy the hover, active etc. states of the selected items. :-/ – bencergazda Apr 19 '16 at 13:15
  • 1
    @bencergazda I haven't tried it, but if you need the :hover states you could trigger a mouseover. Not sure if you could get active. Maybe triggering a mousedown? Give it a try. – Mike Apr 19 '16 at 14:47
  • This code now causes `Error: The operation is insecure` on firefox (chrome still works). This is due to `.cssRules` in `var rules = sheets[i].rules || sheets[i].cssRules;`. You will need try/catch around this line to protect for Firefox. – dean grande Jan 11 '18 at 00:52
  • 1
    Also in firefox I get: "Error: Syntax error, unrecognized expression: unsupported pseudo: hover" Does it take in consideration pseudo classes? – Dalibor Sep 06 '19 at 07:37
  • 1
    Facing `Uncaught DOMException: Failed to read the 'rules' property from 'CSSStyleSheet': Cannot access rules` , what is the issue ? – mehmood khan Oct 01 '20 at 09:07
92

Two years late, but I have the solution you're looking for. Not intending to take credit form the original author, here's a plugin which I found works exceptionally well for what you need, but gets all possible styles in all browsers, even IE.

Warning: This code generates a lot of output, and should be used sparingly. It not only copies all standard CSS properties, but also all vendor CSS properties for that browser.

jquery.getStyleObject.js:

/*
 * getStyleObject Plugin for jQuery JavaScript Library
 * From: http://upshots.org/?p=112
 */

(function($){
    $.fn.getStyleObject = function(){
        var dom = this.get(0);
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            };
            style = window.getComputedStyle(dom, null);
            for(var i = 0, l = style.length; i < l; i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);
                returns[camel] = val;
            };
            return returns;
        };
        if(style = dom.currentStyle){
            for(var prop in style){
                returns[prop] = style[prop];
            };
            return returns;
        };
        return this.css();
    }
})(jQuery);

Basic usage is pretty simple, but he's written a function for that as well:

$.fn.copyCSS = function(source){
  var styles = $(source).getStyleObject();
  this.css(styles);
}
starball
  • 20,030
  • 7
  • 43
  • 238
Dakota
  • 2,915
  • 2
  • 28
  • 25
  • 2
    @Damon: Thanks! I've updated my post, and edited the wording slightly to make it clear that this is not my work. Sorry about the previous wording, I think I typed this answer up late at night, but either way, it was pretty douchey. – Dakota Aug 23 '11 at 05:35
  • 3
    Why does this return `this.css()`? There's no [documentation](http://api.jquery.com/css/) for this method taking no arguments, and if this statement is reached, it throws an exception. I think it would be more appropriate to `return returns;` even if it's an empty object. – Tristan Lee Oct 08 '14 at 18:01
  • 2
    Would it be possible to get a working demo of this? Not clear to me where to put this code and how to invoke it. It's also not clear to me where the output is being stored. Thanks. – Cymro Mar 07 '16 at 18:06
  • 1
    I do not understand how to use it, There's something called jsfiddle that most people use to help. Good programmer are the worse teacher – Gino Jun 13 '16 at 21:10
  • @Gino If you want to copy the style of div1 to div 2 just use: **`$("#div2").copyCSS($("#div1"));`** and to get the style of any element you could use: **`JSON.stringify($('#element').getStyleObject());`** – Wessam El Mahdy Jun 15 '16 at 22:59
  • @Cymro If you want to copy the style of div1 to div 2 just use: **`$("#div2").copyCSS($("#div1"));`** and to get the style of any element you could use: **`JSON.stringify($('#element').getStyleObject());`** – Wessam El Mahdy Jun 15 '16 at 23:00
18

Why not use .style of the DOM element? It's an object which contains members such as width and backgroundColor.

strager
  • 88,763
  • 26
  • 134
  • 176
  • 1
    I'm pretty sure this is the only way to get the actual styles associated with the class. (as opposed to the calculated styles which are different) – cgp Apr 16 '09 at 03:31
  • 35
    With .style you only get properties applied to the style attribute of the element, but not those applied with a CSS class. – acanimal Jun 29 '12 at 18:18
  • +1 as i m using inline script and changing it dynamically afterwords – user991554 Jun 10 '13 at 06:55
11

I had tried many different solutions. This was the only one that worked for me in that it was able to pick up on styles applied at class level and at style as directly attributed on the element. So a font set at css file level and one as a style attribute; it returned the correct font.

It is simple! (Sorry, can't find where I originally found it)

//-- html object
var element = htmlObject; //e.g document.getElementById
//-- or jquery object
var element = htmlObject[0]; //e.g $(selector)

var stylearray = document.defaultView.getComputedStyle(element, null);
var font = stylearray["font-family"]

Alternatively you can list all the style by cycling through the array

for (var key in stylearray) {
console.log(key + ': ' + stylearray[key];
}
Valamas
  • 24,169
  • 25
  • 107
  • 177
  • [In many code samples, getComputedStyle is used from the document.defaultView object. In nearly all cases, this is needless, as getComputedStyle exists on the window object as well. It's likely the defaultView pattern was a combination of folks not wanting to write a testing spec for window and making an API that was also usable in Java.](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) – The Fool May 07 '20 at 22:36
4

@marknadal's solution wasn't grabbing hyphenated properties for me (e.g. max-width), but changing the first for loop in css2json() made it work, and I suspect performs fewer iterations:

for (var i = 0; i < css.length; i += 1) {
    s[css[i]] = css.getPropertyValue(css[i]);
}

Loops via length rather than in, retrieves via getPropertyValue() rather than toLowerCase().

Brandon Hill
  • 1,782
  • 14
  • 12