Related
NOTE: This has an update below. I will likely change description as it is not a getComputedStyle issue, it is an issue actually with print-media and the fact that Internet Explorer now supports document.styleSheets[].cssRules.
I am a bit confused on this as I thought this worked and I am not sure it just recently broke. I am using getComputedStyle which I thought was support in all modern browsers, but I do not get the expected answer with IE 11. Given this code:
getRealStyle: function(elm, attributes) {
var returnObj = {};
var computed = getComputedStyle(elm);
for(var i=0; i<attributes.length; i++) {
returnObj[attributes[i]] = computed[attributes[i]];
}
return returnObj;
},
Where "attributes" is an array of names which I am interested in getting the computed CSS for. It is like this:
attributes: [
'lineHeight',
'alignmentBaseline',
'backgroundImage', 'backgroundPosition', 'backgroundRepeat', 'backgroundColor',
'baselineShift',
'borderTopWidth','borderTopStyle','borderTopColor',
'borderBottomWidth','borderBottomStyle','borderBottomColor',
'borderLeftWidth','borderLeftStyle','borderLeftColor',
'borderRightWidth','borderRightStyle','borderRightColor',
'borderCollapse',
'clear', 'color',
'display', 'direction', 'dominantBaseline',
'fill', 'float',
'fontStyle', 'fontVariant', 'fontWeight', 'fontSize', 'fontFamily',
'height',
'listStyleType', 'listStyleImage',
'marginTop', 'marginBottom', 'marginLeft', 'marginRight','orphans',
'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
'pageBreakAfter', 'pageBreakBefore',
'stroke', 'strokeWidth',
'strokeOpacity', 'fillOpacity',
'tableLayout',
'textAlign', 'textAnchor','textDecoration', 'textIndent', 'textTransform', 'textShadow',
'verticalAlign',
'widows', 'width'],
The only problem I seem to have is "backgroundColor".
If I pass in an "elem" h1 and:
If I set on the "h1" style="background-color:rgb(238, 238, 238);"
The computed background color is returned correctly in IE 11, Chrome, Firefox
If however I set the "h1" style in CSS like this:
h1{
border-top:3px solid #111111;
background-color:rgb(238, 238, 238);
font-size:30px;
padding:3px 10px 3px 0px;
}
The computed background-color in IE only is returned as transparent. Chrome and Firefox do not have his problem.
Even more strange in that sample, "attributes" also contains entries for the border like borderTopColor and that is correctly computed with that code in all browsers including IE.
The page in question is here:
http://www.cloudformatter.com/CSS2Pdf
The code runs when selecting the "Out PDF" button.
If you format this with Chrome, the "h1" at the top of the page in the resulting PDF will have the silver background because the background-color is picked up in getComputedStyle. The border-top will also be there. But if you format in IE11 the background-color will be missing because it is returned as "transparent" but the border will be there and both of these are set in the css.
Similar behavior you can see here http://www.cloudformatter.com/CSS2Pdf.Demos.InlineElements
The "Exception" box is 100% in css. The border works but the color and image does not as they are missing. The font color is also missing as that is set in CSS ... but not everything in CSS is ignored. I even added a few console write's (left is IE, right is Chrome).
IN the above code, I have tried this so far and IE returns "transparent" for the background-color BUT returns the correct color for the border:
getRealStyle: function(elm, attributes) {
var returnObj = {};
var computed = getComputedStyle(elm);
if (elm.localName == 'h1'){
console.log('***** ' + elm.localName + ' *****');
console.log('BackgroundColor: ' + computed.backgroundColor);
console.log('PropValue: ' + computed.getPropertyValue('background-color'));
console.log('BorderTopColor: ' + computed.borderTopColor);
}
for(var i=0; i<attributes.length; i++) {
returnObj[attributes[i]] = computed[attributes[i]];
}
return returnObj;
},
So, am I missing something here or is getComputedStyle not working for IE 11 for something's in external CSS?
UPDATE:
After hours and hours I have isolated the issue as NOT being getComputedStyle. It turns out that IE is working and in fact doing so as we expected in our coding. It is the other browsers that had issues we had not noted until now.
The code uses document.styleSheets[].cssRules to iterate over all CSS and extract print media directives to apply for the PDF formatting. One of those linked CSS files on a remote server is "bootstrap.min.css" and buried within it were CSS rules like no background, all black text, etc.
Well, if you run the code in Firefox and try to access the cssRules, it was actually a caught security error so they are not read. On Chrome, it does not throw any (obvious) error but returns "null". So these browsers "worked" because these CSS rules were never read.
Along comes IE and low and behold, it supports it. It reads the values from a remote CSS without failing or security warning. And because of that, the CSS from "bootstrap.min.css" to do all that stuff was being applied.
So, to fix this for all browsers I only needed to follow the advice here:
Accessing cross-domain style sheet with .cssRules
And then implement rules to skip the print media in certain files (like bootstrap.min.css)
Just so that this question is closed out, you can examine the question above for the update.
As it turns out, some recent version of Internet Explorer now supports document.styleSheets[] and more importantly supports extracting the cssStyles from those styleSheets no matter if they are hosted locally or remotely.
This change caused our code to start reading a remotely hosted CSS style that was not previously read and not noticed because it actually errors in Chrome and Firefox.
So getting access to remotely hosted CSS stylesheets as an object does not require anything with Internet Explorer now (it works without any changes) but does require one to something different in Chrome and Firefox (like setting crossorigin="anonymous" on the link tag).
I found differencies between browsers how they report computed style dimensions when browser window is zoomed. The JSBIN example is in http://jsbin.com/pilohonevo/2/. The code is as follows:
$(window).resize(function()
{
var width1=$(".class1").css("width");
$(".class1").css("width",width1);
var width2="200px";
$(".class2").css("width",width2);
var width3=$(".class3").css("width");
$("#width1").html(width1);
$("#width2").html(width2);
$("#width3").html(width3);
$("#overflow1").html($(".overflow1")[0].scrollWidth);
$("#overflow2").html($(".overflow2")[0].scrollWidth);
$("#overflow3").html($(".overflow3")[0].scrollWidth);
});
When you zoom to minimum by pressing CMD- few times and then back to 100% by pressing CMD+ few times, in Chrome (Mac Version 38.0.2125.111), you get the following values:
The white DIV 1 reports its width as 203px, although DIV 2 and 3 reports 200px. Also scrollWidth is 203, which is wrong as well. This means that you cannot use getComputedStyle or jQuerys .css() to get dimensions if you are not sure that browser window is not zoomed. And because zooming is not cancelable you can never be sure and you can never trust to those dimensions. I tested also $(elem).scrollLeft() and $(elem).scrollTop() and those are unreliable as well when zoomed.
So a workaround can be to use "raw" values, not "computed" values.
Is there a cross-browser javascript or jQuery method to get something like getUnComputedStyle() which determines dimensions using raw values from stylesheets and/or style attribute, because they are the only ones that are zoom-safe?
Determining zoom level and make corrections based on that is unreliable according to my tests, because there are browser differencies and error levels in different style properties are not consistently related to zoom level.
(Firefox Mac 33.1 and Safari Mac version 7.1 (9537.85.10.17.1) and IE 11 Win and emulated modes down to version 7 report correct values.
Opera Mac 25.0.1614.68, Safari Win 5.1.7 and the above reported Chrome report wrong values.)
I've reproduced this with Chrome 49 and JQuery 1.11, not in FF and not in Internet Explorer.
However, I believe this to be an artifact of the code as well. The only divs that show this problem are div1 and overflow1, which both use the same system of setting the width to the computed width, repeatedly. What happens is that for some zooms the computed value is 201. You set the width to 201, so for some zooms the computed value becomes 202 and so on. I got 204, for example.
In the Chrome debugger, at zoom 67%, the reported width appears as 199.981, but the values available to Javascript are integers. scrollWidth is 199 while clientWidth and offsetWidth are 200. All of the jQuery width methods return 200 (width, innerWidth, outerWidth). At zoom 33%, scrollWidth and jQuery widths all return 201, althought the debugger reported width is still 199.981.
My assertion is that the problem is a bug in Chrome and probably related to rounding.
As described here: Getting the actual, floating-point width of an element you can get the actual floating point value reported by the debugger with .getBoundingClientRect(). If you want to be completely safe, try using that one.
If I understand what you are trying to accomplish correctly (and if I don't please say so and I'll try to improve my answer), and assuming you have already managed to catch the zooming event some how (which is not a given), you could:
Clone the div you are trying to get the CSS styles from;
Append the clone to the dom in an unobtrusive way (ie, a way in which it will not cover any other elements on the document);
Remove it's style attribute (just in case it was set by other scripts or functions);
Get all the styles you need from it;
Finally, remove the clone from the dom when you are done.
This demo works for me, regardless of page zoom.
jQuery(function($) {
function getRawStyles(sel, styles) {
sel = $(sel);
var clone = sel.clone().removeAttr("style").attr("class", "cloneDiv").insertBefore(sel);
$.each(styles, function(index, style) {
console.log( style + ": " + $(clone[0]).css(style) );
});
$(".cloneDiv").remove();
}
$(document).ready(function() {
$("button", this).on("click", function() {
getRawStyles("#myDiv", ["height", "width"]);
});
});
});
#myDiv {
background: grey;
height: 50px;
width: 200px;
}
.cloneDiv {
left: -10000;
opacity: 0;
top: -10000;
position: absolute;
z-index: -1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="myDiv" style="height:200px; width: 100px"></div>
<br/>
<button>Log Computed Styles</button>
How can I get the computed width of an element in Opera? In other browsers I can do this:
// getComputedStyle wrapper
function getStyle(element, styleProp) {
return element.currentStyle ? element.currentStyle[styleProp] :
getComputedStyle(element, null).getPropertyValue(styleProp);
}
...but this only sort of works on Opera. It returns "auto" for a lot of things instead of a useful pixel value.
Here's a live demo that expands some text to fit in a box. It doesn't work on Opera, because the computed width is auto instead of a px value as in other browsers.
How can I get more useful computed styles, such as the pixel width of an element, in Opera?
I realize that I can, in this case, use offsetWidth instead of getting the computed style. I appreciate the advice, but the real point of this question is that I want to know how to get computed styles in Opera where the style is actually computed in units. I don't care about offsetWidth for the purposes of this question.
What CSS calls "computed value" isn't always what you expect. I guess Opera follows the spec to the T here while the other browsers do something they consider more useful.
I'd suggest using element.offsetWidth instead of getComputedStyle() for this purpose.
It also fails in IE <= 8
The reason is because currentStyle and getComputedStyle work differently in this case. If you were testing for getComputedStyle first it would work in both Opera and IE 9-10. Opera tries to mimic IE in a lot of cases (see innerText vs textContent), so it has currentStyle too.
BUT please note that you lose your "expected" behavior if the element in question has display:inline in it's style (FF, Chrome, IE), because they will report "auto" for you... except... you guessed it, in Opera which will then show you the "correct" px width of the element.
If you want a general purpose function you better off including a general purpose library (which as you will find are filled with edge cases you will never need). If you have a specific purpose to solve you can use a compatible replacement.
Computed style isn't really useful for you in this case. What you need is probably clientWidth, offsetWidth or scrollWidth depending on your needs. They differ mainly in whether you want to include padding, border, margin and/or clipped areas (in case of horizontally overflowing content).
They are supported even on ancient browsers like IE 6, in fact these properties were first introduced by MS back in the first browser war (just like innerHTML).
You can read more about them by googling with MSDN or MDN.
There shouldn't be any reason this has to be difficult if you keep your code well structured, never put script elements as children to the body element even if it validates as it will lead to very poor coding practices. On the other hand I commend you for using appendChild instead of the unreliable innerHTML so at least you're making an effort to not take the lazy route.
Use an anonymous function for the onload event so you can execute more than one function obviously. I'm not familiar with glyphs (SVG?) so I was not able to get any browser to render the glyph at anything other than 7px.
Here is the reworked code...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>get computed width in pixels in Opera</title>
<script type="application/javascript">
//<![CDATA[
// getComputedStyle wrapper
function getStyle(element, styleProp)
{
return element.currentStyle ? element.currentStyle[styleProp] : getComputedStyle(element, null).getPropertyValue(styleProp);
}
// cheesy convenience function
function textDiv(textContent, className)
{
var tmp = document.createElement('div');
if (className) tmp.className = className;
tmp.appendChild(document.createTextNode(textContent));
return tmp;
}
window.onload = function()
{
var box = document.getElementById('box'),glyph = box.appendChild(textDiv('g', 'glyph')),size=500;
glyph.style.position = 'absolute';
/*
document.getElementById('status').appendChild(textDiv('Initial computed width: ' + getStyle(glyph, 'width')));
while (parseInt(getStyle(glyph, 'width'), 10) < 100)
{
glyph.style.fontSize = size++ + '%';
}
*/
document.getElementById('status').appendChild(document.createTextNode(document.getElementById('box').firstChild.scrollWidth+'px'));
}
//]]>
</script>
</head>
<body>
<div id="status"></div>
<div id="box"></div>
</body>
</html>
You can use this code to get the property in Opera:
document.defaultView.getComputedStyle(element,null).getPropertyValue(styleProp);
I have a problem retrieving the exact css property value (in '%') on firefox.
Suppose we have this extremely simple markup:
<div id="box">box</div>
and this css:
#box{
width:200px;
height:200px;
left:10%;
position:absolute;
background:red;
}
and I'd like to retrieve the left position (in '%') by js
It's obv very easy with mootools (demo -> http://jsfiddle.net/steweb/AWdzB/):
var left = $('box').getStyle('left');
or jQuery (demo -> http://jsfiddle.net/steweb/RaVyU/):
var left = $('#box').css('left');
or by plain js (demo -> http://jsfiddle.net/steweb/tUAKA/):
function getStyle(el,styleProp){ //from ppk's quirksmode
var x = document.getElementById(el);
if (x.currentStyle)
var y = x.currentStyle[styleProp];
else if (window.getComputedStyle)
var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp);
return y;
}
var left = getStyle('box','left');
But if you try it on firefox (8.0.1) you'll see that the result is not correct (should be 10%, but it's 91px). The questions are: is there a bug on this newer version of firefox? Does anyone knows if it's a known bug? Am I doing something wrong?
Thanks :)
Update: I tried it also on older firefox releases, and it's not correct (it always returns px value).. for completeness, it works correctly on IE
This is documented:
The used value of any CSS property is the final value of that property after all calculations have been performed. Used values can be retrieved by calling window.getComputedStyle. Dimensions (e.g. width, line-height) are all in pixels... etc
There seems to be no way to access "specified" css values for a given element, unless you know exactly which css rule applies and parse out this rule using document.stylesheets or similar interface.
The correct answer is a comment on the bug I filed on bugzilla
https://bugzilla.mozilla.org/show_bug.cgi?id=707691#c7
To get the correct % value (on firefox too) the element's (or one of its parents) display should be set to none
Test : http://jsfiddle.net/4RKsM/
The unclear thing is: why on the same browser/version (see, firefox 7 on XP/win7 or Opera 11.5 on mac osx / ubuntu) but on different os, the behav is different?
Btw, the spec #thg435 posted (and reported on mdn) is still in flux.
As I know , it has never shown the percentage (I use ff, opera and chrome). So I think it's not only a firefox problem.
However, you can calculate it manually , but it is just close to the definied value , if the browser window is small.
parseInt($('#box').css('left'))/ $(window).width()*100;
on chrome it depends on the the position value fixed and absolute always give a px whilst other values output what was put in for example if you gave it 10% value then it would output 10% and if you put in 100px then it would output 100px
I am trying to position an span element (let us call it "the tooltip span") relative to a input field. To do this, I am wrapping the tooltip span and the input field in another span element (let's call it "the wrapper span") that has position: relative. Then I set position: absolute on tooltip span. This makes the tooltip span position itself relative to the wrapper span but not being part of the page flow - not taking up any space. This is exactly what I want.
Then, using javascript, I set the position of the tooltip relative to the position of the input element. Since the input element can be shaped differently on different pages (script should be globablly applicable), I am using its offsetTop and offsetLeft properties to calculate its position relative to the wrapper span.
However, I am noticing inconsistencies between browsers here. In Firefox, IE6, 7, 8, it works as expected. But in Chrome and Safari the reported offsetTop seems, well, incorrect.
To prove this, I created the test page below:
<html>
<head>
<style type="text/css">
span { font-size: 8px; position: relative; top: 0; left: 0; border: 1px solid red }
</style>
</head>
<body>
<span id="wrapper">
<input id="foo" name="foo" type="text">
</span>
<script type="text/javascript">
document.write("<br>Offset parent: " + document.getElementById("foo").offsetParent.id);
document.write("<br>Offset top: " + document.getElementById("foo").offsetTop);
</script>
</body>
</html>
and loaded it in Firefox and Chrome. Both browser report the wrapper span as its offsetParent, but for Firefox the offsetTop is -8 and for Chrome it is 2. Visually the page renders the same in both browsers.
This gives me a headache, because I cannot just hack in a different offset that I always apply when someone is using Chrome, because if I change the font size, the offsetTop will not change, and my script will break.
Is this a bug? Can I solve this differently?
You Can try using
$(window).load
instead of
$(document).ready
because Explorer and Chrome sets proper offsets only after images have been fully loaded.
I've been having the same problem as you and I realized that (in my case) the thing that was messing up the offset().top value in chrome, was having one or more images without the "height" attribute above the element.
Before
<img src="/images/foo.jpg" />
offset.top() was 100 in Chrome
offset.top() was 150 in Firefox and IE7 (beleive or not, it worked just fine in IE!)
After
<img src="/images/foo.jpg" height="50" width="50" />
offset.top() is 150 in both Firefox, IE7, AND CHROME.
Notice the the difference was 50px, which is actually the same as the image height.
Since I was developing a JQuery plugin, i tryed to collect all the images that had their width and height attributes undefined, and manually setting their size with .width() and .height(), but it didn't work, because Chrome returned 0 for both functions. So, i guess the problem with offset.top() actually relies on that. If JQuery is triying to get te offset.top() value by accumulating the "above" element's height, and one of those elements it's an image with no specified height, "0" will be added to that sum, and therefore the top value will be missing those "ignored" heights.
PS: Sorry for my english, it's been a long time since i wrote such a long text in this language!
Use jQuery. DOM differences between browsers is one of the things it excels at.
Put you code into a window.onload function. I recall having issues when attempting to work with the dom directly from a <script> during page load in firefox, and webkit tends to be slightly more willing to give a sane DOM at such points.
This is just based on prior issues i've encountered, i'm not sure if it's applicable to your case.
I ran into the same problem, and jQuery's position() function was reporting the same as the offset() function. Ultimately it turns out that even waiting for the document to be ready didn't work for me. I had to check offset() later in the flow (in my case, in my handler that is fired on a window.scroll event).
When I try this test code below, on page load, I get different figures for Firefox + Chrome. Once it loads, however, I can press 'd' and I get the same figure for both browsers.
// this produced different results on Chrome + Firefox (Chrome was wrong!)
$(document).ready(function () {
var x = $('#some-div-on-your-page').position().top;
alert("On load, offset is "+x); // Chrome + Firefox report diff figures
$(window).keydown(function(e, r) {
k = e ? e.keyCode : event.keyCode;
if(k == 68) { // press 'd'
var x = $('#some-div-on-your-page').position().top;
alert("Now the offset is "+x); // ...but this is consistent
}
});
}
Hope this helps.
I was experiencing the same problem, and tried adding to my function
$(document).ready(function(){});
and it worked in both Chrome and Firefox
If you get "0" in chrome, see if you are targeting an empty element like "a". It needs to wrap something in order to return the correct offset.
It could be related to the different border/margin values for the HTML and body elements that browsers set by default.