jQuery: Get height of hidden element in jQuery
Asked Answered
F

15

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();
Freemasonry answered 27/2, 2010 at 0:52 Comment(8)
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.Prynne
possible duplicate of jQuery - Get Width of Element when Not Visible (Display: None)Hulbig
Related: #3632620Haily
I've just found out this hack doesn't work with any version of IE.Impressionable
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?Freemasonry
have you tried using clone() first?Expound
Harry, actually what you found out is that your code doesn't work in IE, not this solution. Go debug your code :)Stillwell
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 debugFlu
S
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 : "");
Sanburn answered 27/2, 2010 at 0:59 Comment(9)
I consolidated the above code into a jQ plugin jsbin.com/ihakid/2. I also use a class and check if the parent/s are also hidden.Ruckus
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.Saphra
+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.Alphaalphabet
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.Snug
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.Stillwell
@Stillwell this prevents a reflow calculation and any flicker to the user, so it's both cheaper and less intrusive.Sanburn
@hitautodestruct, I've modified your query plugin to check this only the element is actually hidden. jsbin.com/ihakid/58Plato
@Prasad Good idea. I think this would be a performance boost if you have a large number of elements to iterate over.Ruckus
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.Anatole
G
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 :)

Gatling answered 5/4, 2011 at 17:16 Comment(4)
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.Ye
Thanks! I used this plugin to read and set the height of elements (this is done dynamically) hidden initially within accordionsScherman
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!Smolder
@Gatling The link is broken.Cassey
S
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.

Strappado answered 27/2, 2010 at 1:26 Comment(2)
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!Martini
Now I understand why it's height not shown with display:none elements. Thanks a lot for wonderful explanation.Geerts
U
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.

Uniparous answered 30/3, 2010 at 21:28 Comment(1)
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.Kurd
P
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:
 * https://mcmap.net/q/109207/-jquery-get-height-of-hidden-element-in-jquery
 *
 * @author Robin van Baalen <[email protected]>
 * @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;
};
Picnic answered 12/1, 2012 at 17:15 Comment(3)
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.Angloamerican
@Angloamerican 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.Picnic
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.Angloamerican
U
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.

Unguent answered 13/4, 2011 at 14:13 Comment(2)
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'}); optionHeight = $("#myDiv").height(); $("#myDiv").css({'position':'','visibility':'', 'display':''});Yare
...unless you already had inline styles on the element. Which would be the case if you hid the element with jQuery.Sod
M
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.

Mosa answered 15/6, 2010 at 23:56 Comment(0)
R
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

Rhiamon answered 20/10, 2013 at 12:15 Comment(2)
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), while visibility changes may be caused also by changes in the class attribute.Voroshilovsk
You're right. The correct statement should be e.attributeName !== 'style' && e.attributeName !== 'className'Rhiamon
A
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.

Alisaalisan answered 23/9, 2013 at 15:6 Comment(1)
inline styles are often used by other JavaScript-features too, therefore resetting the style completely is often not desired.Pickard
R
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).

Rubella answered 27/2, 2010 at 0:55 Comment(3)
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 itFreemasonry
another reason is for centering things with javascript that may not be visible yetPerformance
@Rubella If you just curious write comments, that is very often situation in frontend coding to get size of a hidden element.Candidacandidacy
A
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.

Affiance answered 11/10, 2012 at 15:44 Comment(0)
C
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>
Corrigan answered 9/5, 2015 at 20:47 Comment(0)
C
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
Catherine answered 28/2, 2023 at 11:55 Comment(0)
T
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);
Thirzia answered 30/3, 2010 at 21:31 Comment(1)
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?Cestoid
N
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)

Negation answered 2/8, 2016 at 14:59 Comment(2)
Returns undefinedWivern
Pretty sure this only works if you've already displayed the element on the page previously. For elements that have always been display='none', this won't work.Millsap

© 2022 - 2024 — McMap. All rights reserved.