CSS Computed Style

Programmers who are new to javascript are often delighted to discover that it's remarkably easy to modify the style of some existing DOM element, like so:


var myDiv = document.getElementById('myDiv');
myDiv.style.left = '10em';
myDiv.style.backgroundColor = '#369';

The above code snippet will grab a reference to the element on the page whose id is myDiv, and modify that element's .left css property to '10em' and its .backgroundColor css property to '#369'. Nothing earth-shattering here.

Conversely, javascript programmers are often dismayed that the following may or may not work:


var myDiv = document.getElementById('myDiv');
myDiv.style.top = parseInt(myDiv.style.top) + 100 + 'px';

Notice that I said may or may not work. In what cases will it work and it what cases won't it work? Glad you asked (because the answer is easy). Read on to learn more.

Trying to retrieve the a css style property via javascript will only work when that style property has previously been set via javascript OR when that style property was defined inline by using the style attribute in (X)HTML. Now this doesn't do you much good if you haven't first set said css property in javascript (and I certianly hope no one is using inline css styles), and you still want to retrieve it via javascript.

Enter Computed Style.

What is computed style exactly? Well, according to IE, nothing at all. IE (not surprisingly) doesn't support the W3C DOM standard for retrieving an element's computed style. IE instead uses a proprietary .currentStyle property. Once you're familiar with both implementations though, writing a cross-browser solution isn't too difficult.


if (!window.sstchur) { window.sstchur = {}; }
if (!sstchur.web) { sstchur.web = {}; }
if (!sstchur.web.xb) { sstchur.web.xb = {}; }

sstchur.web.xb.getComputedStyle = function(_elem, _style)
{
  var computedStyle;
  if (typeof _elem.currentStyle != 'undefined')
    { computedStyle = _elem.currentStyle; }
  else
    { computedStyle = document.defaultView.getComputedStyle(_elem, null); }

  return computedStyle[_style];
}

This function isn't too hard to think about. We start by checking for the existence of IE's proprietary .currentStyle property. This violates my normal rule of always trying for W3C properties first, but since the namespace chain for .currentStyle is shorter, it requires less type checking (document.defaultView would fail in IE as would document.default.getComputedStyle).

Once we've determined what browser capabilities we're dealing with, we go head and assign the proper value to the variable, computedStyle. At the end of the function we simply return computedStyle[_style].

I learned the following tip (and the majority of the above as well) from "The Javascript Anthology" by James Edwards & Cameron Adams:

It's important to be aware of browser inconsistencies with regards to computed style. Where most W3C browser's will normalize all property units, IE will return them just as they were originally defined in your stylesheet.

For example, retrieving the computed style of width: 10em; in a W3C browser will yield the equivalent value in pixels. In IE, it will return '10em'.

Similarly, hex color values in W3C browsers will be converted to equivalent rgb(x, x, x) values.

Being able to retrieve a css style property value as it was initially set in a stylesheet is a useful thing indeed. A basic understanding of computed style and the cross-browser function we developed in this blog entry should go a long way towards solving the some of the problems that were discussed at the beginning of this entry.

Also, if you're interested in finding the height or the width of an item (including borders and padding), you're better served with myElem.offsetHeight and myElem.offsetWidth. Those properties will return an integer representing the height or width of the the element (in pixels), and it's best to stick with those properties (as opposed to using the computed style) if you need height and/or width.

As a final note, it's important to realize that retrieving the .top and .left style properties of some element on the page, using our cross-browser getComputedStyle(..) function is not the same as computing an element's absolute position on the page. The former is simply returning a value that was previously set on said style property, while the latter is actually calculating an element's position (sometimes via recursion, sometimes simply by looping). If you're wondering how to calculate an element's position on the page, well… that's another blog entry 😉

But feel free to send comments about this blog entry!

17 thoughts on “CSS Computed Style”

  1. Thanks for the tip.
    I copied the function verbatim and it works well in IE. In Firefox however it does not work. I have a DIV with the following style

    .wrapper {
    margin: 5px 5px 5px 5px;
    padding: 5px 5px 5px 5px;
    background-color:#dfe8f6;
    border: 1px solid magenta;
    }

    Now I tried retrieving the padding and margin from the div using the lines

     var computedStyle = document.defaultView.getComputedStyle(_elem, null);            return computedStyle[_style];  
    

    Does not work in FireFox

    Also tried

     var computedStyle = document.defaultView.getComputedStyle(_elem, null);            return computedStyle.getPropertyValue(_style);  
    

    Wouldnt work either.

    I either case I am passing paddingLeft, paddingRight etc as the property names.

    Any thoughts?

    Thanks,

    Melody

  2. Melody:

    What version of Firefox are you using? You can email me your code if you want, and I can take a more in-depth look at it.

    Also, you might be interested in the Gimme version, .getStyle(..) which does a nice job of equalizing lots of browser quirks. That may or may not solve your problem though — not sure, as I'd have to see more of your code to be sure.

  3. Thanks.

    My code is not pure JavaScript (its GWT JSNI) but here its is.

    The JSNI code

    public static native String JSNIGetStyleProperty(Element _elem, String _style) /*-{
    if (_style=='width') {
    return _elem.offsetWidth;
    }
    if (_style=='height') {
    return _elem.offsetHeight ;
    }
    var computedStyle;
    if (typeof _elem.currentStyle != 'undefined')
    {
    computedStyle = _elem.currentStyle;
    return computedStyle[_style];
    }
    else
    {
    computedStyle = document.defaultView.getComputedStyle(_elem, null);
    return computedStyle[_style];
    }
    }-*/;

    The client code that calls the JSNI code. This is Java (GWT)code

    public static int getElementMarginAndBorderAndPaddingHorz(Element e) {
    int tp = JSNIUtils.getIntStyleProperty(e, "paddingLeft");
    int bp= JSNIUtils.getIntStyleProperty(e, "paddingRight");
    int tbw = JSNIUtils.getIntStyleProperty(e, "borderLeftWidth");
    int bbw = JSNIUtils.getIntStyleProperty(e, "borderRightWidth");
    tp += JSNIUtils.getIntStyleProperty(e, "marginLeft");
    bp += JSNIUtils.getIntStyleProperty(e, "marginRight");
    int w = (tp + bp) + (tbw + bbw);
    return w;
    }

    And the CSS style for the element for which I am trying to determine the element's edges


    .wrapper {
    margin: 5px;
    padding: 5px 5px 5px 5px;
    background-color:#dfe8f6;
    border: 1px solid magenta;
    }

    Also the top of the CSS file says has some magic that may be relevant. The CSS file itself is huge so I will just show snippets of the area that applies.


    @charset "utf-8";
    *{
    -moz-box-sizing:border-box;box-sizing:border-box;margin:0px;padding:0px;
    }
    body {
    color: #000000;
    font-family: Tahoma, Arial, Helvetica, sans-serif;
    font-size: 76%;
    margin: 0px 0px 0px 0px;
    }

    Where can I find the version you are referring to?

    Thanks,

    Melody

  4. Melody:

    I'm not that familiar with GWT (not at all really), but I wonder if, as you pointed out, the -moz specific CSS could be having an effect.

    Have you tried just created a plain HTML page with some similar CSS and tried using my getComputedStyle function in that context? I'd be interested to see if this is somehow related to GWT, or if this is truly a bug in my code (if it's the latter, it definitely needs ot be fixed, but so far it has worked for me).

    The Gimme version comes packaged with the Gimme library: http://codeplex.com/gimme/

    However, you could probably rip out that function by pulling it from the source: http://www.codeplex.com/gimme/SourceControl/FileView.aspx?itemId=168513&changeSetId=18894

    You will also need to grab the function, convertToPixels, as getStyle uses it.

    I would suggest doing some troubleshooting in a plain HTML page first though, before going any further.

  5. Thanks sstchur.
    I tried it in a watered down html page and it works just fine in IE and fireFox. I think there is a problem with the CSS styles on the element when I pass it from GWT to JavaScript using JSNI. It simply wont recognize any properties.

    Back to square one!

    The one thing that I would just add that seems not too clear in your code is that in fireFox, the property name has to be passed as an ecmascript binding value ie. border-bottom, whereas in IE you have to pass the property name without the dash ie. borderBottom. So I found some code somewhere for removing the dash and making the letter after it uppercase.

    getComputedStyle table margin bug

    table{margin:100px;}
    .wrapper {
    margin: 5px;
    padding: 10px 10px 10px 10px;
    background-color:#dfe8f6;/*#c3daf9*/
    border: 1px solid magenta; /*TODO*/
    display: block;
    width: 1920px;width: 1920px !important;
    height: 1022px;height: 1022px !important;
    }

    function getStyle(oElm, strCssRule){
    var strValue = "";
    if(document.defaultView && document.defaultView.getComputedStyle){
    strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
    }
    else if(oElm.currentStyle){
    strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
    return p1.toUpperCase();
    });
    strValue = oElm.currentStyle[strCssRule];
    }
    return strValue;
    }

    YYYYYYYYYYYYYYYY

    Actual margin is set to 100px

    var foo = document.getElementById('wrapper');
    alert('Padding-Right: '+ getStyle(foo, 'padding-right'));
    alert('Margin-Right: '+ getStyle(foo, 'margin-right'));

  6. I noticed that the HTML is not showing so I am trying to paste it without surrounding with blocks

    Here:

    getComputedStyle table margin bug

    table{margin:100px;}
    .wrapper {
    margin: 5px;
    padding: 10px 10px 10px 10px;
    background-color:#dfe8f6;/*#c3daf9*/
    border: 1px solid magenta; /*TODO*/
    display: block;
    width: 1920px;width: 1920px !important;
    height: 1022px;height: 1022px !important;
    }

    function getStyle(oElm, strCssRule){
    var strValue = "";
    if(document.defaultView && document.defaultView.getComputedStyle){
    strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
    }
    else if(oElm.currentStyle){
    strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
    return p1.toUpperCase();
    });
    strValue = oElm.currentStyle[strCssRule];
    }
    return strValue;
    }

  7. Melody,

    Re: the property name has to be passed as an ecmascript binding value

    That is true if you use .getPropertyValue, but I have found that using [styleProperty] has worked well for me in most cases, and all browsers use the property name in camelCase (without the dash), I believe.

  8. I have the snipped code

    in script:
    console.log(cpf.value) // "something"
    this deprecated getElementById function?

    on browsers Chrome 36; firefox and IE11

Leave a Reply

Your email address will not be published. Required fields are marked *