Need to find height of hidden div on page (set to display:none)

82,435

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)

Share:
82,435
Jamis Charles
Author by

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, 2022

Comments

  • Jamis Charles
    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
    Jamis Charles almost 15 years
    I've considered this... This seems like it may be very resource intensive... What do you think?
  • Jamis Charles
    Jamis Charles almost 15 years
    The "hiccup" could be avoided by moving it off the page using negative positioning and using visiblity:hidden possibly
  • Brandon Belvin
    Brandon Belvin almost 15 years
    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.
  • James
    James almost 15 years
    Yes, you would have to - ideally just after the original so any CSS is still intact.
  • kangax
    kangax almost 15 years
    Edited post to answer your concerns (and some thoughts on alternative approach).
  • kangax
    kangax almost 15 years
    That's why element shouldn't be moved; rather - hidden with "visibility:hidden".
  • Brandon Belvin
    Brandon Belvin almost 15 years
    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.
  • kangax
    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
    Brandon Belvin over 14 years
    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.
  • kangax
    kangax over 14 years
    I see now. I missed the fact that you were talking about other elements hiccups :)
  • Jamis Charles
    Jamis Charles over 14 years
    I tried the clone approach and that failed because the width no longer matches up, which messes up the height.
  • Brandon Belvin
    Brandon Belvin over 14 years
    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.
  • dsomnus
    dsomnus almost 12 years
    Brilliant idea. Made it into a jQuery function: stackoverflow.com/a/12463110/373345
  • Travis J
    Travis J over 11 years
    I 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
    Rantiev about 9 years
    What if your parent is hidden?
  • Rantiev
    Rantiev about 9 years
    You would better think about that it's ancestors jsfiddle.net/rantiev/xuumzf9k/2
  • Ashwin Prabhu
    Ashwin Prabhu about 9 years
    Why not just invoke window.getComputedStyle(elem).getPropertyValue("height"), works even when hidden!
  • TommyAutoMagically
    TommyAutoMagically almost 9 years
    Good 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
    Jonathan Marzullo almost 9 years
    @Ashwin Prabhu that works perfectly! As long as css height is defined, and not auto.. Nice!
  • Buttars
    Buttars over 7 years
    I think this would be the best solution. You could even set a z-index if the hidden element gets in the way.
  • Curtis Blackwell
    Curtis Blackwell over 7 years
    Absolute positioning seems unnecessary and affects the height, at least when using paragraphs with default browser styles in Chrome.
  • Claudiu
    Claudiu over 7 years
    The 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
    Gogol over 7 years
    sweet! THanks I think I could implement this. +1 :)
  • Gogol
    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
    Vishal Kumar Sahu over 6 years
    @AshwinPrabhu What is the jQuery way to do window.getComputedStyle(elem).getPropertyValue("height")?
  • Vishal Kumar Sahu
    Vishal Kumar Sahu over 6 years
    I personally don't like any such approach. Sometimes things become uglier.
  • Wolfone
    Wolfone over 5 years
    Still 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
    Yan about 5 years
    Beware: 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
    Kirill over 4 years
    It 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
    Erez Shlomo almost 4 years
    Some 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
    Maciej Sawicki over 3 years
    Before 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