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!

16 Responses

  1. Hosting Best Says:

    DOM is pretty amazing, love the way you can give pretty much everything an ID on your page and then change it with Javascript, especially when it comes to AJAX

  2. Melody Pedzisai Says:

    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

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

    Does not work in FireFox

    Also tried

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

    Wouldnt work either.

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

    Any thoughts?

    Thanks,

    Melody

  3. sstchur Says:

    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.

  4. Melody Pedzisai Says:

    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

  5. sstchur Says:

    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.

  6. Melody Pedzisai Says:

    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'));

  7. Melody Pedzisai Says:

    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;
    }

  8. Melody Pedzisai Says:

    OK so the site is defensive I cant show you the html. You get the idea

  9. sstchur Says:

    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.

  10. Melody Pedzisai Says:

    Yes you are correct. Its a lot neater to do it that way. Thanks much for your help. I am all setup now with a few tweaks on the GWT side.

  11. Bookmarklet to de-cache CSS and images - Version 2 « Zeroed & Noughted Says:

    [...] Getting the current values of styles set via CSS can be a pain. Unless they were explicitly set via JavaScript, we can only fetch them using currentStyle or getComputedStyle, complete of course with their own browser idiosyncrasies. Therefore, the first thing we need is a function to do this for us in a cross-browser fashion. I have amended a version I found on Incoherent Babble. [...]

  12. Dynamic Background Image « My Vietnam Says:

    [...] https://developer.mozilla.org/en/DOM/window.getComputedStyle http://blog.stchur.com/2006/06/21/css-computed-style http://www.hanselman.com/blog/jQueryToShipWithASPNETMVCAndVisualStudio.aspx [...]

  13. Rob Says:

    I had a similar problem the other day requiring the computed style in px not the currentStyle or style value The following function seems to correctly convert the style to px and ems and also handles styles in % and child elements where the parent is in % but the child is set to "auto".

    http://www.strictly-software.com/CSSStyleObject.asp

  14. BLARGH!! for the people » Get Computed Style in JavaScript Says:

    [...] The computed style of an element incorporates external CSS (via stylesheets), inline styles and styles prescribed via JavaScript. It's most commonly used to get the default style of an element if it's not specifically assigned via styles, i.e. the default style that the browser gives an element. It's a very undocumented part of JavaScript engines, and, of course, there are multiple implementations. For more details, read this blog post. [...]

  15. Dynamic Resize by Background Image | while($alive) LiveAndLearn(); Says:

    [...] https://developer.mozilla.org/en/DOM/window.getComputedStyle http://blog.stchur.com/2006/06/21/css-computed-style http://www.hanselman.com/blog/jQueryToShipWithASPNETMVCAndVisualStudio.aspx [...]

  16. Javascript only recognizising inline style and not style set in head | t1u Says:

    [...] Read up on it here: http://blog.stchur.com/2006/06/21/css-computed-style/ [...]

Got something to say?

Please note: Constructive criticism is welcome. Rude or vulgar comments however, are not and will be removed during moderation.