10

I have an external stylesheet that is applying some styles to a given element. I want to be able to move those styles (using JavaScript) to a different element entirely, without having prior knowledge of the styles that are being applied.

The CSS:

td { padding: 5px }
div { }

The HTML:

<td>
    <div>
        Apply the TD styles to this DIV (instead of the TD).
    </div>
</td>

The JavaScript:

$(document).ready(function(){
    $('tr').children('td').each(function(){
        //move the td styles to div
    });
});

How can I achieve this?

Update: To be clear, I have no control over the CSS. I have no way of knowing what styles may be applied. The problem that I'm trying to solve is being able to take an element and copy its styles (which may be unknown) and apply them to a different element.

Andrew
  • 227,796
  • 193
  • 515
  • 708
  • How are you going to select that one element? – Peter Ajtai Sep 27 '10 at 22:46
  • Please provide more details on the use case. In other words, why? –  Jun 09 '13 at 18:36
  • if it is calculated or inherited you won't be able to simply remove a style. A css on a class cannot be just remove like this using `.removeAttr('style')`. you also might find some interesting answers here http://stackoverflow.com/questions/4781410/jquery-how-to-get-all-styles-css-defined-within-internal-external-document-w – TecHunter Jun 11 '13 at 15:17
  • Your question really intrigued me so I did a quick search about how to retrieve CSS computed style. I found this answer. Maybe it is what you are looking for. http://stackoverflow.com/questions/754607/can-jquery-get-all-css-styles-associated-with-an-element – pasine Jun 11 '13 at 20:52

9 Answers9

10

This is a solution I figured out, it mixes document.defaultView.getComputedStyle and jQuery's .css():

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" >            </script>
    <link href="style.css" type="text/css" rel="stylesheet" />
</head>
<body>
    <div id="test" style="background-color: #FF7300">
        <p>Hi</p>
    </div>
    <a href="#" id="button">Apply styles</a>
    <script>
        $("#button").click(function() {
            var element = document.getElementById("test");
             $("#victim").css(document.defaultView.getComputedStyle(element, null));
        });
    </script>
    <div id="victim">
        <p>Hi again</p>
    </div>
</body>

//style.css 
#test {
    border: 3px solid #000000;
}

What this does is copy the computed style object, including the style.css and the inline style, and set it to the other div using jQuery's .css()

I hope I didn't misunderstand the question and that this is what you need.

Edit: Something I forgot is that you asked to 'move' the style rather than copy it. It's very simple to remove styles from the previous div using jQuery's .removeClass() and using .attr() to remove the style attribute, but you have to keep in mind that if there are any style rules being applied to the element's ID, there's no way to keep them from being applied.

vcanales
  • 1,818
  • 16
  • 20
2

I know this will look rough but it works. Initially copying all the computed styles from <td> and applying them to <div> is easy with this plugin from Mike Dunn. The troublesome bit is in removing the styles from the <td> after they have been copied. From what I can tell, the only thing you can do is manually reset them to default with .css().

Working Example

$.fn.copyCSS = function (source) {
    var dom = $(source).get(0);
    var dest = {};
    var style, prop;
    if (window.getComputedStyle) {
        var camelize = function (a, b) {
                return b.toUpperCase();
        };
        if (style = window.getComputedStyle(dom, null)) {
            var camel, val;
            if (style.length) {
                for (var i = 0, l = style.length; i < l; i++) {
                    prop = style[i];
                    camel = prop.replace(/\-([a-z])/, camelize);
                    val = style.getPropertyValue(prop);
                    dest[camel] = val;
                }
            } else {
                for (prop in style) {
                    camel = prop.replace(/\-([a-z])/, camelize);
                    val = style.getPropertyValue(prop) || style[prop];
                    dest[camel] = val;
                }
            }
            return this.css(dest);
        }
    }
    if (style = dom.currentStyle) {
        for (prop in style) {
            dest[prop] = style[prop];
        }
        return this.css(dest);
    }
    if (style = dom.style) {
        for (prop in style) {
            if (typeof style[prop] != 'function') {
                dest[prop] = style[prop];
            }
        }
    }
    return this.css(dest);
};

$('td').click(function () {
    $('div').copyCSS('td');

    $('td').removeAttr('style');

    $("td").css({
        'font-family': 'none',
            'font-size': 'none',
            'font-weight': 'none',
            'font-style': 'none',
            'color': '#000',
            'text-transform': 'none',
            'text-decoration': 'none',
            'letter-spacing': 'none',
            'word-spacing': 'none',
            'line-height': 'none',
            'text-align': 'none',
            'vertical-align': 'none',
            'direction': 'none',
            'background-color': 'transparent',
            'background-image': 'none',
            'background-repeat': 'none',
            'background-position': 'none',
            'background-attachment': 'none',
            'opacity': '1',
            'width': 'none',
            'height': 'none',
            'top': '',
            'right': '',
            'bottom': '',
            'left': '',
            'margin-top': '0',
            'margin-right': '0',
            'margin-bottom': '0',
            'margin-left': '0',
            'padding-top': '0',
            'padding-right': '0',
            'padding-bottom': '0',
            'padding-left': '0',
            'border-top-width': '0',
            'border-right-width': '0',
            'border-bottom-width': '0',
            'border-left-width': '0',
            'border-top-color': '0',
            'border-right-color': '0',
            'border-bottom-color': '0',
            'border-left-color': '0',
            'border-top-style': '0',
            'border-right-style': '0',
            'border-bottom-style': '0',
            'border-left-style': '0',
            'position': 'static',
            'display': '',
            'visibility': 'visible',
            'z-index': 'auto',
            'overflow-x': 'auto',
            'overflow-y': 'auto',
            'white-space': 'normal',
            'clip': 'auto',
            'float': 'none',
            'clear': 'none',
            'cursor': 'default',
            'list-style-image': 'none',
            'list-style-position': '0',
            'list-style-type': 'disc',
            'marker-offset': '',
            'padding': '0',
            'transition': 'none',
            'border-radius': 'none'
    });
});

Note: obviously I have not included a reset for all possible styles.

Community
  • 1
  • 1
apaul
  • 16,092
  • 8
  • 47
  • 82
  • That's some of the most horrible code I've seen in a long time. Why not just create a new stylesheet rule based on window.getComputedStyle (it returns a `CSSStyleDeclaration` which can you can use for the purpose), and set that class on the target element? Also, I'm not sure the OP meant by "moving" styles that the original element should be completely reset to a blank slate. Why would he want that? –  Jun 09 '13 at 18:34
  • @torazaburo I know it looks horrible... and honestly I can't really think of a good use case for it, but judging from the question and some of the [comments](http://stackoverflow.com/questions/3808400/how-to-move-all-computed-css-styles-from-one-element-and-apply-them-to-a-differe/16995257?noredirect=1#comment4039343_3808413) on the other answers it looked as if the OP was looking to remove all styles from one element and apply them to another. I would love to see a better way to do it, if you know one, post an answer. – apaul Jun 09 '13 at 20:27
2

EDIT: This answer was written before the question was clarified.

Give your elements IDs, like this:

<td id="td_element" style="padding:5px">
    <div id="div_element">
        Apply the TD styles to this DIV (instead of the TD).
    </div>
</td>

And use

var td_element = $('#td_element')
$('#div_element').attr('style', td_element.attr('style'));
td_element.removeAttr('style');

Of course you could use a class instead - or you may have the elements from previous javascript code. You'll need to decide the best way to reference the elements yourself.

SamStephens
  • 5,721
  • 6
  • 36
  • 44
  • will this work if the styles are being applied from an external stylesheet? – Andrew Sep 27 '10 at 22:53
  • 1
    No it doesn't, as that's not what you asked for. As you can see from this example, applying the styles is easy. However working out the styles applied to an element including from stylesheets is rather tricky. See this question: http://stackoverflow.com/questions/395341/get-element-stylesheet-style-in-javascript. I'd take the advice contained in the accepted answer for that post: "Perhaps you would do better to step back and ask why you would ever need such a thing?" – SamStephens Sep 27 '10 at 23:00
  • and @Andrew if the styles were defined through classes that were net restricted to `td` elements like `td.someclass{..}` then you could remove the class from the `td` and apply it to the `div`. Look at [addClass()](http://api.jquery.com/addClass/) and [removeClass()](http://api.jquery.com/removeClass/) – Gabriele Petrioli Sep 27 '10 at 23:03
  • I've rephrased the question to better reflect what I am trying to ask. – Andrew Sep 27 '10 at 23:08
1

EDIT: Code corrected

$('div').attr('style', $('td').attr('style'));

If you want to move just add this line afterwards

$('td').attr('style', '');

I realize that this looks contrived but I have no other selectors to work with here. Normally you'd want to do this with an id or a class instead of the actual attribute.

Chuck Vose
  • 4,560
  • 24
  • 31
1

Don't move the styles is my short answer - find another way. Can you load another stylesheet after the stylesheet being loaded? If so, add a stylesheet that overrides the td and div padding defined in the stylesheet you're having to reference.

SamStephens
  • 5,721
  • 6
  • 36
  • 44
  • sure, there's lots of different ways you can move the styles, but the point of the question is to find a way using javascript – Andrew Jun 10 '13 at 05:11
  • Surely the point of the question is to solve whatever problem you have? I'm suggesting that trying to move the styles using javascript will be both painful and brittle, and if you can find a way to solve your problem without doing this, you'll have a better and more durable solution. – SamStephens Jun 10 '13 at 18:12
  • The problem I was trying to solve was solved a long time ago using a different approach. I'm asking the question because I would like to know the answer to using this specific technique. – Andrew Jun 10 '13 at 19:20
  • Ahhh, in that case my advice remains the same to anyone else who might consider doing this in future. That is, don't do it, find another way :-) – SamStephens Jun 10 '13 at 21:39
0

What about applying the actual TD style to a class? Then just modify the class of the TD and the DIVs. Or you dont have access to the stylesheet?

The CSS:

.class_name { padding: 5px }

The HTML:

<td class="class_name">
    <div>
        Apply the TD styles to this DIV (instead of the TD).
    </div>
</td>

The JavaScript:

$(document).ready(function(){
    $('.class_name').removeClass('class_name').children('div').addClass('class_name');
});
Alvaro
  • 9,247
  • 8
  • 49
  • 76
0

You can try the following steps to copy the styles defined for particular element in stylesheets and <style> tags ( code is little bit lengthy ):

  1. Get the contents of original stylesheets using ajax. and contents of each <style> tags
  2. Parse the css contents into array
  3. For each selectors in the parsed stylesheet, compare it's dom element with target dom element ( from which you are copying the styles ). if any of them matches , then apply styles corresponding to mathced selector to the required element

Here is the code:

<html>
    <head>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
        <style>
        #source { width: 100px; height: 100px; background-color: black; }
        </style>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
       <table>
            <tr>
                <td id="source"></td>
            </tr>
       </table>
       <div id="target"></div>
    </body>

    <script>
    var source = $('#source'); // source
    var target = $( '#target' ); // target

    $( 'link[rel="stylesheet"]' ).each( function() {
          $.get( $(this).attr( 'href' ), {}, function( data ) {
                 var css_obj = parse_css( data );
                 copy_css( css_obj, source, target );
          });
    });

    $( 'style' ).each( function() {
        var css = $(this).text();
        var css_obj = parse_css( css );
        copy_css( css_obj, source, target );
    })

    function copy_css( css_obj, source, target ) {
        for( selector in css_obj ) {
            if( $(selector)[0] === source[0] ) { // compare raw dom elements 
                for( prop in css_obj[selector] ) {
                    target.css( prop, css_obj[selector][prop] );
                }
            }                             
        }
    }


    function parse_css( css ) { 
        var css = css.replace( /[\s\n\t]+/g, ' ' ); // remove extra spaces, tabs and newlines
        css = css.replace( /\/\*(.|\n)+\*\//g, '' ); // remove comments
        css = css.split( '}' );  // split into groups
        var css_obj = []; // array to hold parsed css

        for( i in css ) {
            if( css[i] == false ) continue;
            var chunks = css[i].split( '{' );
            var selector = $.trim( chunks[0] );
            var style_defs = [];
            chunks = chunks[1].split( ';' );
            for( j in chunks ) {
                if( chunks[j] == false ) continue;
                var style_def = chunks[j].split( ':' );
                var property = $.trim( style_def[0] );
                var value = $.trim( style_def[1].replace( /\;$/, '' ) );
                style_defs[property] = value;
            }
            css_obj[selector] = style_defs;
        }
        return css_obj;
    }

    </script>

Prakash GPz
  • 1,675
  • 4
  • 16
  • 27
0

Here's how I do it:

var elFrom = document.getElementById('fromID');
var computedStyle = document.defaultView.getComputedStyle(elFrom,null);
$.each(computedStyle,function(key,value) {
    $('#toID').css(value,$(elFrom).css(value));
});
Matt H
  • 6,422
  • 2
  • 28
  • 32
-1

And a more generalized solution from the provided answers..

HTML

<td style="padding:5px">
    <div class="inherit">
        Apply the TD styles to this DIV (instead of the TD).
    </div>
</td>

JS

$('.inherit')
     .attr('style', $(this)
                    .parents('td')
                    .attr('style')
          )
     .parents('td')
     .removeAttr('style');
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317