upshots.org

4Aug/1117

jQuery – copy style (copyCSS)

UPDATE: this is now available in the jQuery plugin repository: http://plugins.jquery.com/project/copyCSS

In response to the surprising popularity of my very simple getStyleObject method, here's a slight update, and another extension to simplify copying styles to another element.

First, the updated method to get back all styles (inherited, native, descended, inline, etc) of the invoking element:

$.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;
    };
    if(style = dom.style){
      for(var prop in style){
        if(typeof style[prop] != 'function'){
          returns[prop] = style[prop];
        };
      };
      return returns;
    };
    return returns;
}

And the helper to copy (all styles of @source are passed to invoker):

$.fn.copyCSS = function(source){
  var styles = $(source).getStyleObject();
  this.css(styles);
}

Or all in one:

$.fn.copyCSS = function(source){
    var dom = $(source).get(0);
    var style;
    var dest = {};
    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);
            dest[camel] = val;
        };
        return this.css(dest);
    };
    if(style = dom.currentStyle){
        for(var prop in style){
            dest[prop] = style[prop];
        };
        return this.css(dest);
   };
   if(style = dom.style){
      for(var prop in style){
        if(typeof style[prop] != 'function'){
          dest[prop] = style[prop];
        };
      };
    };
    return this.css(dest);
};

// usage...
// $('#my-element').copyCSS('#another-element');
// or...
// $('#my-element').copyCSS(someReferenceToAnElement);

[wrapping this in (function($){})(jQuery) does not count as writing a plugin - tsk tsk] :)

Comments (17) Trackbacks (3)
  1. Wowee!
    that saved me much work! Would have saved me a whooole lot if I’d found it earlier, too! thanks for putting this together, it’s invlauable.

  2. Hi,

    I would like to use this in a project, but as we have strict rules around licences for snippets copied from blog posts etc at work, I need to know whether you’d consider releasing it under a permissible licence that would allow me to use it for commercial work (MIT or similar is fine).

    PC

  3. Nice snippet, but it seems to mess up JQuery version 1.7.1

    line 12: var camel = prop.replace(/\-([a-z])/g, camelize);

    The above line causes an error: “prop.replace is not a function”

    If you change it to: var camel = prop.toString().replace(/\-([a-z])/g, camelize);

    It then removes the error but the errors occur in the JQuery’s file where .replace is used. Not sure if this is a JQuery error though

    • Hmm.. Thanks for pointing that out – so toString suppresses the local error but doesn’t fix the problem? I’ll take a look – feel free to post back if you find a fix.

    • I just ran a quick test on jsfiddle (http://jsfiddle.net/b9sN7/1/) and it seems to be working fine… I’m guessing that there was something else wrong with the script that produced the error you’re talking about (possibly an unmatched selector?). If you want to post the code, I’d be happy to take a look

  4. This is exactly what I’ve been looking for for the past 2 hours this morning. THANK YOU! Unfortunately, it looks like some overzealous DBA* has purged the Plugin site so your plugin doesn’t exist there at the moment. Are you planning on re-submitting?

    * Not criticizing – I’ve done the same thing plenty of times.

    • Glad it could help. AFAIK the plugin site is still down (I think they’re moving to a different CMS or host…) When it’s back up I’ll likely resubmit.

  5. =/

    Since when are if() conditions made with single “=” ..its if (x == y)..

    • actually, what’s posted is correct. if you used evaluation there (as you suggest), it’d fail. the concept demonstrated is pretty simple.

      instead of saying:

      var style = dom.currentStyle;
      if(style != null) {
        // ...
      

      you can condense it into a single line:

      if(style = dom.currentStyle) {
        // ...
      

      This construct is used in many languages… You’ll see this in Java quite a bit, for example:

      while((someVariable = someMethod()) != null) {
        // do stuff with someVariable
      
  6. Opera seems to have a bit of trouble with the prop loop (0 length) and some inline properties (eg style=”float:right;” isn’t seen)..?

    • Interesting, thanks for pointing that out. I don’t do much with Opera personally – if you have any suggested fixes, please post.

      • Can’t say I use Opera much either, but it was in my test batch ;)
        There’s 2 problems with Opera: the lenght of the style object is 0, and it doesn’t seem to support getPropertyValue (or it does but not like the other browsers do anyway).

        So here’s a quick hack that that i’ve tested in FF, IE7-9, chrome/safari, opera. There’s probably a better way of writing it but it ‘just works’. line 10, replace:

                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);
                    dest[camel] = val;
                };
        

        With:

                    if (style.length)
                        for(var i = 0, l = style.length; i < l; i++){
                            var prop = style[i];
                            var camel = prop.replace(/\-([a-z])/, camelize);
                            var val = style.getPropertyValue(prop);
                            returns[camel] = val;
                        }
                    else
                        for(var prop in style){
                            var camel = prop.replace(/\-([a-z])/, camelize);
                            var val = style.getPropertyValue(prop)  || style[prop];
                            returns[camel] = val;
                        }
        

        The first block is your code unchanged, and 2nd block is slightly modified for opera.
        Your function has saved me a lot of time btw, thanks a lot for sharing!


Leave a comment


*