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:
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:
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 (!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 Responses
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
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
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.
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
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.
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'));
I noticed that the HTML is not showing so I am trying to paste it without surrounding with
blocksHere:
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;
}
OK so the site is defensive I cant show you the html. You get the idea
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.
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.
[…] 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. […]
[…] 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 […]
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
[…] 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. […]
[…] 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 […]
[…] Read up on it here: http://blog.stchur.com/2006/06/21/css-computed-style/ […]
I have the snipped code
in script:
console.log(cpf.value) // "something"
this deprecated getElementById function?
on browsers Chrome 36; firefox and IE11