Retrieve width/height of a css3 scaled element - javascript

I'm fighting against an oddity (I think) of the offsetWidth property.
this is the scenario:
I've got, let's say, a span tag, in my js, at a certain point I perform a css3 transform
to this element, like:
el.set('styles', {
'transform':'scale('+scale+', '+scale+')', /* As things would be in a normal world */
'-ms-transform':'scale('+scale+', '+scale+')', /* IE 9 */
'-moz-transform':'scale('+scale+', '+scale+')', /* Moz */
'-webkit-transform':'scale('+scale+', '+scale+')', /* Safari / Chrome */
'-o-transform':'scale('+scale+')' /* Oprah Winfrey */
});
w = el.getWidth();
console.log('after scaling: ' + w);
at this point the log returns me fuzzy values, like it doesn't know what to say.
any suggestion?

getBoundingClientRect() returns the correct values for me.
jsFiddle.

Transforms don't affect the intrinsic CSS properties, so you are seeing correct behavior. You need to look at the Current Transformation Matrix - as returned from getComputedStyle().webkitTransform in order to figure out how big something has been scaled.
Update: In Firefox 12 and later and Chrome/Safari - as Alex says below, you can use getBoundingClientRect() to take into account any transforms applied to an element
The scale transition starts from 50%/50% because that's the default & correct behavior. If you want it to start from the origin, then you need to set transform-origin: 0% 0%;

I take it the span tag has display:block? transforms are only supposed to work for block elements. When I goto console and load jquery (just for ease) and do this:
$('span#foo').css({"-webkit-transform": "scale(2.0, 2.0)"})
nothing happens. But if I do this:
$('span#foo').css('display','block').css({"-webkit-transform": "scale(2.0, 2.0)"})
suddenly it changes, and offsetHeight increases. Disappointingly, the offset hight goes from 16 to 19, and not to 32 (although the visual appearance is double the size and maybe it is accurate) — but at least it does appear to work as advertised.

getBoundingClientRect() didn't work for me (in Chrome 49).
This answer led me to another solution to my problem: https://stackoverflow.com/a/7894886/1699320
function getStyleProp(elem, prop){
if(window.getComputedStyle)
return window.getComputedStyle(elem, null).getPropertyValue(prop);
else if(elem.currentStyle) return elem.currentStyle[prop]; //IE
}
getStyleProp(element, 'zoom') //gives zoom factor to use in calculations

Related

outerHeight in Chrome giving wrong value, OK in IE and FireFox

In the jquery.smartWizard plugin, there is a function called fixHeight which adjusts the height of a wizard step. This is used when a step is first displayed or when revealing hidden divs within the step. It works fine in IE (at least in IE 11 on Win8.1) and in FireFox. But, in the latest version of Chrome (Version 40.0.2214.94 m) the outerHeight is a much smaller value than it should be, by over 100 pixels or more.
This is the function, out of the box:
SmartWizard.prototype.fixHeight = function(){
var height = 0;
var selStep = this.steps.eq(this.curStepIdx);
var stepContainer = _step(this, selStep);
stepContainer.children().each(function() {
if($(this).is(':visible')) {
height += $(this).outerHeight(true);
}
});
// These values (5 and 20) are experimentally chosen.
//stepContainer.height(height);
//this.elmStepContainer.height(height + 12);
stepContainer.animate({ "height": height - 12 }, 500);
this.elmStepContainer.animate({ "height": height }, 500);
alert(window.outerHeight);
}
I modify the final steps to add the animation. With or without Chrome fails.
EDIT:
Here is a fiddle that demonstrates the difference between IE and Chrome. Click member, then click non-member. You will see that second set of values is different in each browser.
http://jsfiddle.net/xjk8m8b1/
EDIT2:
Here is another fiddle that shows both browsers get the same values for height until you try and calculate the visible elements. Then Chrome is way off.
http://jsfiddle.net/xjk8m8b1/2/
While not the best solution, I did figure out the issue. Firefox and IE are both adding up the height of everything in the div, include break tags and anything that creates vertical space. Chrome, in my opinion is broken, and not adding up these extra elements! It is not returning a true value for consumed vertical space.
My workaround is to wrap the contents of the div inside another dummy div. This way jquery looks at the height of that first child div and correctly returns the height.
I have the same problem, a ScrollBar is in the middle, the StepContainer never fixes the height.
Then I change this line in jquery.smartwizard.js:
$this.elmStepContainer.height(_step($this, selStep).outerHeight());
To this:
$this.elmStepContainer.height(_step($this, selStep).outerHeight() +20);
20 is enough for me, and my problem is gone.

Choppy scrolling in Safari

I have a problem with my micro website. When I scroll, it's nice and smooth in all browsers except Safari. When I do scroll in Safari, the content div jumps or moves frequently (it should stay in place) and makes the scrolling look choppy. Do you have any idea what could be wrong?
This is the website:
http://beta.dynamicdust.com
I haven't checked to see how my answer compares to Jack's, but I think the problem is that Safari attempts to be very power efficient. As a result, it is hesitant to enable hardware acceleration unless it needs to. A common trick that people use to force hardware acceleration is to place
-webkit-transform: translate3d(0, 0, 0);
into the css for the divs which are moving. I tried it with the content class and it seemed a little better. You might try applying it to the other layers as well.
EDIT: I applied it to the left and right text holder divs as well, and the page seems just as smooth as Chrome now.
I took a look and did see the "choppy" scrolling you mentioned (looking at it more, it was hit or miss - sometimes it was smooth, other times it was VERY choppy).
It seems you've got some performance issues with your parallax callback on Safari (though it wouldn't surprise me if it's some buggy implementation with Safari...)
One thing I would recommend is taking a look at requestAnimationFrame for webkit. For a test, I wrapped the logic to update the offsets in a raf (and cached the window.pageYOffset value) and it seemed smoother on my end.
function parallax(e) {
window.webkitRequestAnimationFrame(function() {
var offset = window.pageYOffset;
a.style.top = (offset / 2) + "px";
b.style.top = (offset / 2) + "px";
textbox.style.top =- (offset * 0.7) + "px";
textbox2.style.top =- (offset * 0.7) + "px";
});
}
To be honest, you could probably use raf for all browsers (if they support it).
Another trick people use when animating elements is to accelerate the layer that the element you are animating is on. There are a few ways to do this, but the easiest is to use -webkit-transition and set translateZ(0). It wouldn't hurt to add the two additional lines as well:
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
Also, I noticed that some elements you offset (using style) are position: relative - Personally, I'd say that any element that's to be animated should be position: absolute. This will remove the element from the DOM, and offsetting it won't cause reflows to surrounding elements (which may contribute to your choppiness).
Edit - one other thing I noticed is that "choppiness/weirdness" happens when you encounter rubberbanding on safari (my guess are the negative values). That might be something you'll want to look at as well.
Good luck!
Just jotting this down, as I came across this today with an overflow auto element:
The solution was to add this rule to whatever element the scrollbar shows up on:
-webkit-overflow-scrolling: touch;
More detail can be found here:
https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling
Elements with heavy transitions on hover and active state can result in rendering issues.
In our case we had box-shadow transitions on some elements that were applied on hover. When the user scrolled the page and swiped the effected element, the transition got triggered. The browser then had to do the hard work of rendering the transition while scrolling.

jQuery example (in jsfiddle) working in firefox but not in IE8, 7

Why this example not working in IE http://jsfiddle.net/8RZVt/
I'm getting this error in IE8.
Message: Invalid argument.
Line: 156
Char: 295
Code: 0
URI: http://code.jquery.com/jquery-1.4.4.min.js
According to jQuery, this is because, as stated on the animate documentation page:
All animated properties should be a
single numeric value (except as noted
below); properties that are
non-numeric cannot be animated using
basic jQuery functionality....
So, in fact, in Firefox you are using undefined behavior. The correct thing to do would be to animate on backgroundPositionX, however Firefox does not support this.
There is, however, a jQuery plugin that does what you want:
http://plugins.jquery.com/project/backgroundPosition-Effect
Update
On closer inspection, the plugin does not support += or -= formats.
I hacked it into this example:
http://jsfiddle.net/CxqSs/ (See new example at bottom.)
Could definitely use some cleanup, and should probably be added to that plug-in, but it works in both browsers and doesn't rely on undefined behavior.
BTW, I don't know if it's worth noting, but if you leave this animation running a long time, it will eventually overflow the value and break. This could be overcome by animating the full length of the background image and then resetting the offset to 0px in the callback before the next animate. This would also avoid needing the += format.
Also,
It should be noted that speed: 1, step: 1 and speed: 50, step: 50 are equivalent.
The reason they look different speeds is because
There is more overhead in a speed of 1 (which is really a millisecond duration) because animate gets called more often.
The default easing is "swing", meaning that the animation speeds up and slows down slightly throughout it's course, meaning that the overall speed is affected a bit. You should change the easing to "linear" for your scrolling case:
var animate = function() {
element.animate({
...
}, speed, "linear", animate);
};
This means that you could use the backgroundPosition-Effect plugin, without the '+=', by setting your step to 2247 (the width of the image), like I stated above.
And that finally brings us to... wait for it...
http://jsfiddle.net/zyQj3/20/
Cross-platform, non-kludgy, non-overflowing, correctly easing, extra parameter-lacking, solution.
The script fails at this point because you are passing an invalid CSS value:
element.animate({
backgroundPosition: animStep + " 0px" /* evaluates to "+=50px 0px" */
}, speed, animate);
OK here we go again :D
http://jsfiddle.net/c7rKV/1/
Again identical to original however again just animating backgroundPositionX when in IE.
Apologies on not actually looking at FF/Chrome last time.
Additionally: this of course is not very graceful and Adam Prax is absolutely correct on what the problem is. I just wanted to post a solution to it.
If you check the source code of jQuery, you will see it uses this regexp to parse the parameter (which in your case is +=50px 0px). So it will see it as += (increase) 50 (to fifty) px 0px (unit, append after the number). When trying to read the current value, jQuery uses parseFloat, which just grabs the number at the start of the string. So it works perfectly, even if a multi-dimensional property is probably not what the jQuery programmers had in mind.
Except that IE8 does not support getting the current value of background-position. There is background-position-x and background-position-y but no background-position. Duh. So your best bet is checking the browser type, and animating either background-position or background-position-x depending on that: http://jsfiddle.net/22UWW/
(There is actually a jQuery bug report about this, with a more elegant solution.)

Getting element position in IE versus other browsers

We all know IE6 is difficult. But there seems to be disparate behavior in positioning in later versions of IE as well, when compared with Firefox or other browsers. I have a simple pair of javascript functions which finds the position of an element, and then displays another element in relation to the first element. The idea is to get the second element, which is somewhat larger, to appear in front of the first element when the mouse hovers over it. It works fine, except on all versions of Internet Explorer, the position of the second element appears different than in Firefox.
The code to get the position of an element is:
function getPosition(e)
{
var left = 0;
var top = 0;
while (e.offsetParent) {
left += e.offsetLeft;
top += e.offsetTop;
e = e.offsetParent;
}
left += e.offsetLeft;
top += e.offsetTop;
return {x:left, y:top};
}
And the actual rollover display code is:
var pos = getPosition(elem1);
elem2.style.top = pos.y - 8;
elem2.style.left = pos.x - 6;
In Firefox, elem2 appears directly over elem1, as I want it to. But in IE7 or IE8 it appears way off. What is the reason this occurs, and is there a way to fix it?
elem2.style.top = pos.y - 8;
CSS requires a unit. +'px'.
(There could conceivably be differences between IE and other browsers depending on how exactly elem2 is positioned.)
As Pointy commented the best thing is to go and look up the jQuery (or YUI that is probably more readable) code. There are normalization needed mainly by the IE quirksmode (but it's not the only issue). For instance (but I'm not sure) I think you need to add to the left/top total amount borders' size of each positioned absolute/relative elements you encounter in the while loop, but in IE6 you need to add borders only if position is absolute at least in quirksmode.
Your code might work without adding more normalization only if you use the DOCTYPE (either Transitional or Strict) as very 1st line of your HTML pages and you reset body/html border/margins and padding. In other words use this line at the beginning of your pages:
<!DOCTYPE...
and in your CSS:
html, body {margin: 0; padding: 0; border-width: 0;}
These anyway might not suffice.

Fix for background-position in IE

I get this problem in IE7 when running a piece of code that uses jquery and 2 jquery plugins. The code works in FF3 and Chrome.
The full error is:
Line: 33
Char: 6
Error: bg is null or not an object
Code: 0
URL: http://localhost/index2.html
However line 33 is a blank line.
I am using 2 plugins: draggable and zoom. No matter what I do to the code it is always line 33 that is at fault. I check the source has update via view source but I feel this could be lying to me.
<body>
<div id="zoom" class="zoom"></div>
<div id="draggable" class="main_internal"><img src="tiles/mapSpain-smaller.jpg" alt=""></div>
<script type="text/javascript">
$(document).ready(function() {
$('#draggable').drag();
$('#zoom').zoom({target_div:"draggable", zoom_images:new Array('tiles/mapSpain-smaller.jpg', 'tiles/mapSpain.jpg') });
});
</script>
</body>
Essentially what I am trying to do is recreate the Pragmatic Ajax map demo with jQuery.
It would appear that the second line of this snippet is causing the trouble:
bg = $(this).css('background-position');
if(bg.indexOf('%')>1){
It seems to be trying to select the background-position property of #draggable and not finding it? Manually adding a background-position: 0 0; didn't fix it. Any ideas on how to get around this problem?
I tried using the MS Script Debugger but that is nearly useless. Can't inspect variables or anything else.
A bit more digging about on the Interweb has revealed the answer: IE doesn't understand the selector background-position. It understands the non-standard background-position-x and background-position-y.
Currently hacking something together to workaround it.
Nice one, Redmond.
To get around the fact that Internet Explorer does not support the "background-position" CSS attribute, as of jQuery 1.4.3+ you can use the .cssHooks object to normalize this attribute between browsers.
To save yourself some time, there is a background position jQuery plugin available that allows both "background-position" and "background-position-x/y" to work as expected in browsers that don't support one or the other by default.
It is interesting. IE8 doesn't understand getter backgroundPosition, but it understands setter.
$('.promo3').mousewheel(function(e,d){
var promo3 = $(this);
var p = promo3.css('backgroundPosition');
if (p === undefined) {
p = promo3.css('backgroundPositionX') + ' ' + promo3.css('backgroundPositionY');
}
var a = p.split(' ');
var y = parseInt(a[1]);
if (d > 0) {
if (y < -1107) y += 1107;
y -= 40;
}
else {
if (y > 1107) y -= 1107;
y += 40;
}
promo3.css('backgroundPosition', a[0] + ' ' + y + 'px');
return false;
});
It works great in IE8 and IE8 compatible view.
This worked for me:
if (navigator.appName=='Microsoft Internet Explorer')
{
bg = $(drag_div).css('backgroundPositionX') + " " + $(drag_div).css('backgroundPositionY');
}
else
{
bg = $(drag_div).css('background-position');
}
hope it does for you.
You may want to check to make sure that you are loading your js files in the correct order so that any dependencies are taken into account.
A bit of thinking (and a cup of tea) later I came up with:
if(bg == 'undefined' || bg == null){
bg = $(this).css('background-position-x') + " " + $(this).css('background-position-y');
}
Unfortunately it returns center center despite the online resources I can find state it should return 0 0 if the values are undefined.
Beginning to wonder if there is an actual fix/workaround to this. A lot of people have tried and all so far fail to catch all edge cases.
The camelCase version of backgroundPosition seems viable but I don't know enough of jQuery to make an accurate assessment of how to go about it - from what I have read you can only use camelCase as getters if the property has been set previously. Please tell me if I am mistaken.
However line 33 is a blank line.
It'll be line 33 of one of your .js files, not line 33 of the HTML itself. IE fails to report which actual file the error was in. Look at line 33 of each .js for something about ‘bg’; if the worst comes to the worst you can start inserting newlines at the start of each .js and see whether the line number changes.
I check the source has update via view source but I feel this could be lying to me.
View source will always show you what IE got from the server. It won't show any updates to the DOM.
try backgroundPosition istead
Also, make sure that 'this' exists and that your request for an attribute returns a value. IE will throw this kind of errors when you try to call a method on a property that does not exist, therefore bg is null or null an object. if you dont care about IE you can do bg = $(this)... || '' so that theres always something referenced.
Also, unrelated to the error you're getting, but is your index value of 1 correct? Did you mean -1 ?
Yupp, Try background-position instead or just set the background-position with jquery before you call it. Ill guess one often knows the positions through CSS before calling it. It isnt pretty, but somehow it did the trick for me.)
eg:
//set it in with javascript.
$("someid").css("background-position", "10px 0");
...
//do some funky stuff
//call it
$("someid").css("background-position");
//and it would return "10px 0" even in IE7
if nothing helps, it's also possible to make the following trick.
We can replace a background of an element by an inner absolutely positioned element (with the same background). The coordinates will be replaced by left and top properties. This will work in all browsers.
For better understanding, please, check the code:
Before
<div></div>
div {
background: url(mySprite.png);
background-position: -100px 0;
}
After
<div>
<span></span>
</div>
div {
position: relative;
overflow: hidden;
width: 100px; /* required width to show a part of your sprite */
height: 100px; /* required height ... */
}
div span {
position: absolute;
left: -100px; /* bg left position */
top: 0; /* bg top position */
display: block;
width: 500px; /* full sprite width */
height: 500px; /* full sprite height */
background: url(mySprite.png);
}
This solution is not very flexible, but it helped me to show icons hover state properly.
You can't use dashes in the jquery css function. You have to do it in camelCase:
.css('backgroundPosition') or .css('backgroundPositionX') and .css('backgroundPositionY') for IE

Categories

Resources