260

I need to get height of an element that is within a div that is hidden. Right now I show the div, get the height, and hide the parent div. This seems a bit silly. Is there a better way?

I'm using jQuery 1.4.2:

$select.show();
optionHeight = $firstOption.height(); //we can only get height if its visible
$select.hide();
mkoryak
  • 57,086
  • 61
  • 201
  • 257
  • 9
    I disagree Tim. With this solution there is a chance that the display might flicker because you are actually showing the item and then hiding it. Even though Nicks solution is more convoluted, it has no chance of flickering the display as the parent div is never shown. – Humphrey Jul 24 '11 at 23:59
  • possible duplicate of [jQuery - Get Width of Element when Not Visible (Display: None)](http://stackoverflow.com/questions/1472303/jquery-get-width-of-element-when-not-visible-display-none) – DocMax Feb 19 '13 at 04:05
  • Related: http://stackoverflow.com/questions/3632120/jquery-height-width-and-displaynone – Nick Feb 21 '13 at 17:03
  • I've just found out this hack doesn't work with any version of IE. – Harry Apr 26 '13 at 15:37
  • This hack works with IE because I used it there before. Maybe the parent div is hidden and so showing the element has no effect? – mkoryak Apr 26 '13 at 16:55
  • have you tried using clone() first? – Bakaburg Aug 29 '13 at 11:06
  • 5
    Harry, actually what you found out is that your code doesn't work in IE, not this solution. Go debug your code :) – Gavin Sep 12 '13 at 21:42
  • if the element whose height you are trying to calculate has the attribute of max-height:30em and contains content greater than that height, you will get different values at different times, making it hard to debug – Silver Moon Jan 14 '20 at 09:58

15 Answers15

189

You could do something like this, a bit hacky though, forget position if it's already absolute:

var previousCss  = $("#myDiv").attr("style");

$("#myDiv").css({
    position:   'absolute', // Optional if #myDiv is already absolute
    visibility: 'hidden',
    display:    'block'
});

optionHeight = $("#myDiv").height();

$("#myDiv").attr("style", previousCss ? previousCss : "");
Antti29
  • 2,953
  • 12
  • 34
  • 36
Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • 17
    I consolidated the above code into a jQ plugin http://jsbin.com/ihakid/2/. I also use a class and check if the parent/s are also hidden. – hitautodestruct Dec 29 '11 at 09:54
  • This is truly the best solution. Because the element is visible for the browser to correctly calculate its size but the element is not visible to a web user. Gotta love that visibility attribute. – Dwayne Charrington Jan 11 '12 at 22:36
  • 3
    +1 This works great. Just keep in mind the parents of the element must be displayed as well. This had me confused for a few minutes. Also, if the element is `position:relative`, you'll of course want to set that after instead of `static`. – Michael Mior Mar 16 '12 at 05:11
  • 6
    When you use this method, the target element can often change shape. So getting the height and/or width of the div when it's absolutely positioned may not be very useful. – Jeremy Ricketts Jul 06 '12 at 21:20
  • I'm failing to see how this is better than showing and hiding the div? First of all, if your DIV's parent is not set to position relative or absolute, and your div uses a percentage for it's dimension, it will return incorrect values. Just show() your DIV, get your dimensions, then hide() your DIV again. Everybody uses that method and there's no problems with it. Changing an elements position property to get it's dimensions is not logical. – Gavin Sep 12 '13 at 21:45
  • 2
    @Gavin this prevents a reflow calculation and any flicker to the user, so it's both cheaper and less intrusive. – Nick Craver Sep 13 '13 at 09:42
  • 2
    @hitautodestruct, I've modified your query plugin to check this only the element is actually hidden. http://jsbin.com/ihakid/58/ – Prasad K - Google Jan 20 '14 at 13:33
  • @Prasad Good idea. I think this would be a performance boost if you have a large number of elements to iterate over. – hitautodestruct Jan 20 '14 at 13:43
  • Note: This won't work if a parent is hidden. You need to apply this change to all parents as well. An easy way it use the Jquery actual plugin listed in the answer below. – Rafi Apr 02 '14 at 20:36
99

I ran into the same problem with getting hidden element width, so I wrote this plugin call jQuery Actual to fix it. Instead of using

$('#some-element').height();

use

$('#some-element').actual('height');

will give you the right value for hidden element or element has a hidden parent.

Full documentation please see here. There is also a demo include in the page.

Hope this help :)

Community
  • 1
  • 1
ben
  • 1,525
  • 2
  • 15
  • 15
  • 3
    Yes it helps! 2015 and it still helps. You say "older versions of jquery" on the Actual website - however - it is still required in v2.1.3, at least it was in my situation. – LpLrich Mar 30 '15 at 14:34
  • Thanks! I used this plugin to read and set the height of elements (this is done dynamically) hidden initially within accordions – alds Jun 08 '16 at 17:02
  • Awesome plugin! I tried everything to get the width of a div inside a modal when set to 100% and nothing worked. set this up in 5 seconds and worked perfectly! – Craig Howell Sep 06 '17 at 13:17
  • @ben The link is broken. – Sachin Prasad Oct 11 '19 at 10:28
41

You are confuising two CSS styles, the display style and the visibility style.

If the element is hidden by setting the visibility css style, then you should be able to get the height regardless of whether or not the element is visible or not as the element still takes space on the page.

If the element is hidden by changing the display css style to "none", then the element doesn't take space on the page, and you will have to give it a display style which will cause the element to render in some space, at which point, you can get the height.

Brian
  • 376
  • 4
  • 9
casperOne
  • 73,706
  • 19
  • 184
  • 253
  • thanks for this. my problem involved a table cell, and setting `visibility` to `hidden` didn't solve my problem, but it gave me the idea to set `position: fixed; top: 100%` and it works like a charm! – Jayen Jun 04 '14 at 10:32
  • Now I understand why it's height not shown with display:none elements. Thanks a lot for wonderful explanation. – FrenkyB Sep 02 '17 at 08:17
30

I've actually resorted to a bit of trickery to deal with this at times. I developed a jQuery scrollbar widget where I encountered the problem that I don't know ahead of time if the scrollable content is a part of a hidden piece of markup or not. Here's what I did:

// try to grab the height of the elem
if (this.element.height() > 0) {
    var scroller_height = this.element.height();
    var scroller_width = this.element.width();

// if height is zero, then we're dealing with a hidden element
} else {
    var copied_elem = this.element.clone()
                      .attr("id", false)
                      .css({visibility:"hidden", display:"block", 
                               position:"absolute"});
    $("body").append(copied_elem);
    var scroller_height = copied_elem.height();
    var scroller_width = copied_elem.width();
    copied_elem.remove();
}

This works for the most part, but there's an obvious problem that can potentially come up. If the content you are cloning is styled with CSS that includes references to parent markup in their rules, the cloned content will not contain the appropriate styling, and will likely have slightly different measurements. To get around this, you can make sure that the markup you are cloning has CSS rules applied to it that do not include references to parent markup.

Also, this didn't come up for me with my scroller widget, but to get the appropriate height of the cloned element, you'll need to set the width to the same width of the parent element. In my case, a CSS width was always applied to the actual element, so I didn't have to worry about this, however, if the element doesn't have a width applied to it, you may need to do some kind of recursive traversal of the element's DOM ancestry to find the appropriate parent element's width.

casperOne
  • 73,706
  • 19
  • 184
  • 253
elliotlarson
  • 301
  • 2
  • 3
  • This has an interesting problem though. If the element is already a block element, the clone'll take the entire body width (except body padding or margin), which is different than it's size inside a container. – Meligy Feb 02 '12 at 01:53
17

Building further on user Nick's answer and user hitautodestruct's plugin on JSBin, I've created a similar jQuery plugin which retrieves both width and height and returns an object containing these values.

It can be found here: http://jsbin.com/ikogez/3/

Update

I've completely redesigned this tiny little plugin as it turned out that the previous version (mentioned above) wasn't really usable in real life environments where a lot of DOM manipulation was happening.

This is working perfectly:

/**
 * getSize plugin
 * This plugin can be used to get the width and height from hidden elements in the DOM.
 * It can be used on a jQuery element and will retun an object containing the width
 * and height of that element.
 *
 * Discussed at StackOverflow:
 * http://stackoverflow.com/a/8839261/1146033
 *
 * @author Robin van Baalen <robin@neverwoods.com>
 * @version 1.1
 * 
 * CHANGELOG
 *  1.0 - Initial release
 *  1.1 - Completely revamped internal logic to be compatible with javascript-intense environments
 *
 * @return {object} The returned object is a native javascript object
 *                  (not jQuery, and therefore not chainable!!) that
 *                  contains the width and height of the given element.
 */
$.fn.getSize = function() {    
    var $wrap = $("<div />").appendTo($("body"));
    $wrap.css({
        "position":   "absolute !important",
        "visibility": "hidden !important",
        "display":    "block !important"
    });

    $clone = $(this).clone().appendTo($wrap);

    sizes = {
        "width": $clone.width(),
        "height": $clone.height()
    };

    $wrap.remove();

    return sizes;
};
Robin van Baalen
  • 3,632
  • 2
  • 21
  • 35
  • your code is not correct. You need `sizes = {"width": $wrap.width(), "height": $wrap.height()};` the `this` refers to the original item, which is not visible. – jackJoe Jun 15 '16 at 14:31
  • @jackJoe I have been using this code for quite some time without issues. At best I would say I need $clone.width() instead of 'this'. Not $wrap since I want the size of the element inside the wrap. Wrapper could be 10000x10000px while the element I want to measure is still 30x40px for example. – Robin van Baalen Jun 15 '16 at 14:35
  • Even targetting the `$wrap` is ok (at least in my tests). I tried with the `this` and obviously it wouldn't catch the size because `this` still refers to the hidden element. – jackJoe Jun 15 '16 at 16:19
12

Building further on Nick's answer:

$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'});
optionHeight = $("#myDiv").height();
$("#myDiv").css({'position':'static','visibility':'visible', 'display':'none'});

I found it's better to do this:

$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'});
optionHeight = $("#myDiv").height();
$("#myDiv").removeAttr('style');

Setting CSS attributes will insert them inline, which will overwrite any other attributes you have in your CSS file. By removing the style attribute on the HTML element, everything is back to normal and still hidden, since it was hidden in the first place.

Gregory Bolkenstijn
  • 10,003
  • 7
  • 36
  • 38
  • 1
    `$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'}); optionHeight = $("#myDiv").height(); $("#myDiv").css({'position':'','visibility':'', 'display':''});` – Aaron Apr 27 '11 at 19:23
  • 9
    ...unless you already had inline styles on the element. Which would be the case if you hid the element with jQuery. – Muhd Oct 13 '11 at 19:48
4

You could also position the hidden div off the screen with a negative margin rather than using display:none, much like a the text indent image replacement technique.

eg.

position:absolute;
left:  -2000px;
top: 0;

This way the height() is still available.

vaughanos
  • 521
  • 5
  • 10
3

I try to find working function for hidden element but I realize that CSS is much complex than everyone think. There are a lot of new layout techniques in CSS3 that might not work for all previous answers like flexible box, grid, column or even element inside complex parent element.

flexibox example enter image description here

I think the only sustainable & simple solution is real-time rendering. At that time, browser should give you that correct element size.

Sadly, JavaScript does not provide any direct event to notify when element is showed or hidden. However, I create some function based on DOM Attribute Modified API that will execute callback function when visibility of element is changed.

$('[selector]').onVisibleChanged(function(e, isVisible)
{
    var realWidth = $('[selector]').width();
    var realHeight = $('[selector]').height();

    // render or adjust something
});

For more information, Please visit at my project GitHub.

https://github.com/Soul-Master/visible.event.js

demo: http://jsbin.com/ETiGIre/7

user229044
  • 232,980
  • 40
  • 330
  • 338
  • Thanks for your answer. It inspired me to use MutationObservers too. But notice that your library checks only for visibility changes caused by `style` attribute changes (see line 59 [here](https://github.com/Soul-Master/visible.event.js/blob/master/src/visible.event.js)), while visibility changes may be caused also by changes in the `class` attribute. – Ashraf Sabry Sep 23 '14 at 11:59
  • You're right. The correct statement should be e.attributeName !== 'style' && e.attributeName !== 'className' –  Sep 24 '14 at 05:52
2

Following Nick Craver's solution, setting the element's visibility allows it to get accurate dimensions. I've used this solution very very often. However, having to reset the styles manually, I've come to find this cumbersome, given that modifying the element's initial positioning/display in my css through development, I often forget to update the related javascript code. The following code doesn't reset the styles per say, but removes the inline styles added by javascript:

$("#myDiv")
.css({
    position:   'absolute',
    visibility: 'hidden',
    display:    'block'
});

optionHeight = $("#myDiv").height();
optionWidth = $("#myDiv").width();

$("#myDiv").attr('style', '');

The only assumption here is that there can't be other inline styles or else they will be removed aswell. The benefit here, however, is that the element's styles are returned to what they were in the css stylesheet. As a consequence, you can write this up as a function where an element is passed through, and a height or width is returned.

Another issue I've found of setting the styles inline via js is that when dealing with transitions through css3, you become forced to adapt your style rules' weights to be stronger than an inline style, which can be frustrating sometimes.

Prusprus
  • 7,987
  • 9
  • 42
  • 57
  • inline styles are often used by other JavaScript-features too, therefore resetting the style completely is often not desired. – David May 06 '21 at 08:51
1

In my circumstance I also had a hidden element stopping me from getting the height value, but it wasn't the element itself but rather one of it's parents... so I just put in a check for one of my plugins to see if it's hidden, else find the closest hidden element. Here's an example:

var $content = $('.content'),
    contentHeight = $content.height(),
    contentWidth = $content.width(),
    $closestHidden,
    styleAttrValue,
    limit = 20; //failsafe

if (!contentHeight) {
    $closestHidden = $content;
    //if the main element itself isn't hidden then roll through the parents
    if ($closestHidden.css('display') !== 'none') { 
        while ($closestHidden.css('display') !== 'none' && $closestHidden.size() && limit) {
            $closestHidden = $closestHidden.parent().closest(':hidden');
            limit--;
        }
    }
    styleAttrValue = $closestHidden.attr('style');
    $closestHidden.css({
        position:   'absolute',
        visibility: 'hidden',
        display:    'block'
    });
    contentHeight = $content.height();
    contentWidth = $content.width();

    if (styleAttrValue) {
        $closestHidden.attr('style',styleAttrValue);
    } else {
        $closestHidden.removeAttr('style');
    }
}

In fact, this is an amalgamation of Nick, Gregory and Eyelidlessness's responses to give you the use of Gregory's improved method, but utilises both methods in case there is supposed to be something in the style attribute that you want to put back, and looks for a parent element.

My only gripe with my solution is that the loop through the parents isn't entirely efficient.

marksyzm
  • 5,281
  • 2
  • 29
  • 27
1

By definition, an element only has height if it's visible.

Just curious: why do you need the height of a hidden element?

One alternative is to effectively hide an element by putting it behind (using z-index) an overlay of some kind).

cletus
  • 616,129
  • 168
  • 910
  • 942
  • 1
    i need a height of an element, that happens to be hidden during the time i want its height. i am building a plugin and i need to gather some data as i init it – mkoryak Feb 27 '10 at 00:57
  • 2
    another reason is for centering things with javascript that may not be visible yet – Simon_Weaver Feb 04 '11 at 00:52
  • @cletus If you just curious write comments, that is very often situation in frontend coding to get size of a hidden element. – Rantiev Apr 08 '15 at 16:35
1

One workaround is to create a parent div outside the element you want to get the height of, apply a height of '0' and hide any overflow. Next, take the height of the child element and remove the overflow property of the parent.

var height = $("#child").height();
// Do something here
$("#parent").append(height).removeClass("overflow-y-hidden");
.overflow-y-hidden {
  height: 0px;
  overflow-y: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="parent" class="overflow-y-hidden">
  <div id="child">
    This is some content I would like to get the height of!
  </div>
</div>
Matt
  • 5,800
  • 1
  • 44
  • 40
1

To obtain the actual height of a hidden element, an alternative approach is to retrieve the height of the scroll view by using the following code:

$firstOption[0].scrollHeight
idimitrov
  • 41
  • 5
0

Here's a script I wrote to handle all of jQuery's dimension methods for hidden elements, even descendants of hidden parents. Note that, of course, there's a performance hit using this.

// Correctly calculate dimensions of hidden elements
(function($) {
    var originals = {},
        keys = [
            'width',
            'height',
            'innerWidth',
            'innerHeight',
            'outerWidth',
            'outerHeight',
            'offset',
            'scrollTop',
            'scrollLeft'
        ],
        isVisible = function(el) {
            el = $(el);
            el.data('hidden', []);

            var visible = true,
                parents = el.parents(),
                hiddenData = el.data('hidden');

            if(!el.is(':visible')) {
                visible = false;
                hiddenData[hiddenData.length] = el;
            }

            parents.each(function(i, parent) {
                parent = $(parent);
                if(!parent.is(':visible')) {
                    visible = false;
                    hiddenData[hiddenData.length] = parent;
                }
            });
            return visible;
        };

    $.each(keys, function(i, dimension) {
        originals[dimension] = $.fn[dimension];

        $.fn[dimension] = function(size) {
            var el = $(this[0]);

            if(
                (
                    size !== undefined &&
                    !(
                        (dimension == 'outerHeight' || 
                            dimension == 'outerWidth') &&
                        (size === true || size === false)
                    )
                ) ||
                isVisible(el)
            ) {
                return originals[dimension].call(this, size);
            }

            var hiddenData = el.data('hidden'),
                topHidden = hiddenData[hiddenData.length - 1],
                topHiddenClone = topHidden.clone(true),
                topHiddenDescendants = topHidden.find('*').andSelf(),
                topHiddenCloneDescendants = topHiddenClone.find('*').andSelf(),
                elIndex = topHiddenDescendants.index(el[0]),
                clone = topHiddenCloneDescendants[elIndex],
                ret;

            $.each(hiddenData, function(i, hidden) {
                var index = topHiddenDescendants.index(hidden);
                $(topHiddenCloneDescendants[index]).show();
            });
            topHidden.before(topHiddenClone);

            if(dimension == 'outerHeight' || dimension == 'outerWidth') {
                ret = $(clone)[dimension](size ? true : false);
            } else {
                ret = $(clone)[dimension]();
            }

            topHiddenClone.remove();
            return ret;
        };
    });
})(jQuery);
casperOne
  • 73,706
  • 19
  • 184
  • 253
eyelidlessness
  • 62,413
  • 11
  • 90
  • 94
  • The answer by eyelidlessness looks perfect for what I need but doesn't work with the jQuery I am running: 1.3.2 It causes jQuery to throw a this.clone (or something very close) is not a function. Anyone have any ideas on how to fix it? –  Mar 04 '11 at 20:17
0

If you've already displayed the element on the page previously, you can simply take the height directly from the DOM element (reachable in jQuery with .get(0)), since it is set even when the element is hidden:

$('.hidden-element').get(0).height;

same for the width:

$('.hidden-element').get(0).width;

(thanks to Skeets O'Reilly for correction)

Luca C.
  • 11,714
  • 1
  • 86
  • 77