views:

734

answers:

7

I need to measure the offsetHeight of a div that is inside of a hidden element.

<div id="parent" style="display: none;">
    <div id="child">Lorem Ipsum dolor sit amet.</div>
</div>

The parent div must be set to "display:none". I have no control over that. I realize that the offsetHeight of the child div is going to be 0. I need to find a workaround.

Something I've toyed with is when the page loads, I copy the childnodes of parent, inject in a div on the page that is set to "visiblity:hidden". Then I measure the height of those elements, and remove the nodes when done.

Any other thoughts?

+3  A: 

You could clone the element, absolutely position it at -10000,-10000, measure the clone and destroy it.

lod3n
If I clone it, I'd have to inject it, right?
Jamis Charles
Yes, you would have to - ideally just after the original so any CSS is still intact.
J-P
+3  A: 

You need to make element's parent visible for that one very short moment while you're getting element's dimensions. In a generic solution, all ancestors are usually traversed and are made visible. Then their display values are set back to original ones.

There are performance concerns of course.

We considered this approach in Prototype.js implementation but ended up with getWidth and getHeight making only actual element visible, without traversing ancestors.

The problem with alternative solutions - such as taking element out of "hidden" parent - is that certain styles might no longer apply to an element once it's out of its "regular" hierarchy. If you have a structure like this:

<div class="foo" style="display:none;">
  <div class="bar">...</div>
</div>

and these rules:

.bar { width: 10em; }
.foo .bar { width: 15em; }

then taking element out of its parent will actually result in wrong dimensions.

kangax
I've considered this... This seems like it may be very resource intensive... What do you think?
Jamis Charles
Edited post to answer your concerns (and some thoughts on alternative approach).
kangax
A: 

Use z-index to hide element under non-transparent element, show it, and get height.

Anatoliy
A: 

Until the element is rendered, it has no height. Even if you clone the parent object and display it somewhere that can't be seen by the user, there's not guarantee that the clone will have the same height as the final size of the hidden object.

There are many things that can affect the height that wouldn't necessarily be rendered in the clone - anything in the DOM and its interaction with the CSS rules could cause a change in rendering any other element of the DOM. Short of cloning the entire document (and even that's not fool-proof) you have no way of determining the height of the hidden object.

If you must know the height before it's displayed to the user, you'll have to "hack" it by displaying it for as short of a time as possible then hiding it again. Most likely, the user will see this hiccup and not be pleased by the result.

Brandon Belvin
The "hiccup" could be avoided by moving it off the page using negative positioning and using visiblity:hidden possibly
Jamis Charles
But if you move it out of its true position on the page, you cannot _guarantee_ that the height will be correct. The only way you can be 100% sure it's right is to display it in-place. Moving it elsewhere might give you the correct answer, but then you have to accept the possibility of a rendering difference.
Brandon Belvin
That's why element shouldn't be moved; rather - hidden with "visibility:hidden".
kangax
But putting it in `visibility:hidden` can cause offset of other elements by adding the element back into the DOM. You still risk getting the hiccup, even though the element causing the hiccup will remain hidden.
Brandon Belvin
@Brandon how will it cause hiccups if "visibility:hidden" merely affects **visibility** of an element and not its layout?
kangax
Because the browser will carve out the room to display the item when it's no longer `display:none`. `visibility:hidden` tells the browser to not display the item, but it will set aside the room to display it. Elements that share the same z-order as the element that formerly was marked `display:none` have the potential of being moved, resized, etc because of this element "re-appearing" into the rendered DOM.
Brandon Belvin
I see now. I missed the fact that you were talking about **other elements** hiccups :)
kangax
I tried the clone approach and that failed because the width no longer matches up, which messes up the height.
Jamis Charles
Which is exactly what I noted. You cannot necessarily get the proper height without rendering it in-place. I believe my original post answers your question completely and correctly.
Brandon Belvin
A: 

So, you cannot even change the display:none; to height:0; overflow:hidden; ? Maybe you could override that in your own stylesheet like so:

div#parent { display: block !important; height:0; overflow:hidden; }

And then as you are using YUI (assuming YUI 2) you could use this:

var region = YAHOO.util.Dom.getRegion('child');

To get the dimensions and offset of the child.

Max
A: 

What I wound up having to do was this:

Using YUI 2, on page load, I found all elements of that given classname that were either set to display:none, or whose height and width was 0 (that's one way of measuring whether an element exists, or a parent is set to display:none). I then set that element to display:block. I then checked it's parent for the same thing and showed the parents until it finds a visible parent. Once highest display:none ancestor is set to display:block, I can measure my element.

Once all elements are measured I reset all of the elements back to display:none.

Jamis Charles
A: 

Did you try this ?

setTimeout('alert($(".Var").height());',200); 
Paolo Mulder