Need to find height of hidden div on page (set to display:none)
Solution 1
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.
Solution 2
If you use style.display = "none"
, the element will have 0 width and height,
but using the style.visibility = "hidden"
instead, the element will have the width and height calculated by the browser (as normally).
Solution 3
A workaround is to set the height
to 0
.hidden {
height: 0;
overflow: hidden;
}
Then to get the elements scrollHeight
.
document.querySelector('.hidden').scrollHeight
The scrollHeight will correctly give you the height though the element does not appear. I don't think it affects element flow either.
Example: https://jsfiddle.net/de3vk8p4/7/
Reference: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements#How_big_is_the_content.3F
Solution 4
You could clone the element, absolutely position it at -10000,-10000, measure the clone and destroy it.
Solution 5
Made a pure js solution with no Jquery and with no cloning (which I guess is faster)
var getHeight = function(el) {
var el_style = window.getComputedStyle(el),
el_display = el_style.display,
el_position = el_style.position,
el_visibility = el_style.visibility,
el_max_height = el_style.maxHeight.replace('px', '').replace('%', ''),
wanted_height = 0;
// if its not hidden we just return normal height
if(el_display !== 'none' && el_max_height !== '0') {
return el.offsetHeight;
}
// the element is hidden so:
// making the el block so we can meassure its height but still be hidden
el.style.position = 'absolute';
el.style.visibility = 'hidden';
el.style.display = 'block';
wanted_height = el.offsetHeight;
// reverting to the original values
el.style.display = el_display;
el.style.position = el_position;
el.style.visibility = el_visibility;
return wanted_height;
}
here is the demo https://jsfiddle.net/xuumzf9k/1/
Please let me know if you can find any improvements to this (as I use this in my main projects)
Jamis Charles
I'm a Full Stack Node.js developer. I work at PayPal (Express, Kraken, React.js, Webpack, Backbone.js)
Updated on July 09, 2022Comments
-
Jamis Charles almost 2 years
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?
Update: 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 almost 15 yearsI've considered this... This seems like it may be very resource intensive... What do you think?
-
Jamis Charles almost 15 yearsThe "hiccup" could be avoided by moving it off the page using negative positioning and using visiblity:hidden possibly
-
Brandon Belvin almost 15 yearsBut 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.
-
James almost 15 yearsYes, you would have to - ideally just after the original so any CSS is still intact.
-
kangax almost 15 yearsEdited post to answer your concerns (and some thoughts on alternative approach).
-
kangax almost 15 yearsThat's why element shouldn't be moved; rather - hidden with "visibility:hidden".
-
Brandon Belvin almost 15 yearsBut 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. -
kangax almost 15 years@Brandon how will it cause hiccups if "visibility:hidden" merely affects visibility of an element and not its layout?
-
Brandon Belvin over 14 yearsBecause 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 markeddisplay:none
have the potential of being moved, resized, etc because of this element "re-appearing" into the rendered DOM. -
kangax over 14 yearsI see now. I missed the fact that you were talking about other elements hiccups :)
-
Jamis Charles over 14 yearsI tried the clone approach and that failed because the width no longer matches up, which messes up the height.
-
Brandon Belvin over 14 yearsWhich 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.
-
dsomnus almost 12 yearsBrilliant idea. Made it into a jQuery function: stackoverflow.com/a/12463110/373345
-
Travis J over 11 yearsI could not see any flickering while minutely showing the element. I took the approach of only showing it while getting the height, then hiding it. That is also what jQuery's slideToggle() does. It works nicely. +1
-
Rantiev about 9 yearsWhat if your parent is hidden?
-
Rantiev about 9 yearsYou would better think about that it's ancestors jsfiddle.net/rantiev/xuumzf9k/2
-
Ashwin Prabhu about 9 yearsWhy not just invoke window.getComputedStyle(elem).getPropertyValue("height"), works even when hidden!
-
TommyAutoMagically almost 9 yearsGood idea! But if the element is already set to
display: none
, why not just make the necessary temporary changes to the element itself, measure it, and then undo the changes to move it back to the original position? -
Jonathan Marzullo almost 9 years@Ashwin Prabhu that works perfectly! As long as css height is defined, and not auto.. Nice!
-
Buttars over 7 yearsI think this would be the best solution. You could even set a z-index if the hidden element gets in the way.
-
Curtis Blackwell over 7 yearsAbsolute positioning seems unnecessary and affects the height, at least when using paragraphs with default browser styles in Chrome.
-
Claudiu over 7 yearsThe absolute positioning is used (when hidden) especially not to affect any other layout. The position is reverted before the element gets its original visibility.
-
Gogol over 7 yearssweet! THanks I think I could implement this. +1 :)
-
Gogol over 7 years@rinogo that might cause unnecessary flicker in screen as the element gets shown for a very short time. Cloning it with an absolute position is a much better solution as you are not touching the original element atall.
-
Vishal Kumar Sahu over 6 years@AshwinPrabhu What is the jQuery way to do
window.getComputedStyle(elem).getPropertyValue("height")
? -
Vishal Kumar Sahu over 6 yearsI personally don't like any such approach. Sometimes things become uglier.
-
Wolfone over 5 yearsStill a nice piece of code; I like the simplicity. Thanks for sharing! Though I use scrollHeight instead of offsetHeight, feeling that it might suit my case better. I am not sure whether this is the most intelligent thing to do. I feel that depending on the use-case even a mixture would be nice...one way or the other I would prefer to have a configurable spec-method for this.
-
Yan about 5 yearsBeware: this method restores style attributes from the computed style element (i.e with already applied css classes). So, if you remove a specific class from your element, it will still have the styles of the removed class.
-
Kirill over 4 yearsIt doesn’t exactly answer the question (
display: none
) but it helped me to resolve similar issue. To know the actual height you might need to also take padding into consideration. -
Erez Shlomo almost 4 yearsSome users might experience a transparent square where the div is hidden (because the div's height didn't change) after setting visibility to hidden. You can add max-height:0;overflow:hidden; and then use the element's scrollHeight
-
Maciej Sawicki over 3 yearsBefore showing the element you can temporarily add overflow:hidden and max-height: 0 to its style, then measure its scrollHeight - this way you are sure there won't be any flickering, as the measured element is never really displayed