Foliotek Development Blog

Getting the width of a hidden element with jQuery using width()

Monday, December 7th, 2009

UPDATE #3: It needs to be noted that the fix introduced in jQuery 1.4.4 is only for the width() and height() methods. ?If you need inner/outer dimensions the method below still needs to be used. ?I have updated the method to return height/width, outer height/width, and inner height/width. ?There is an optional parameter to include margins in the outer dimension calculations. ?Thanks Ryan and Fred for the heads up.

UPDATE #2: jQuery 1.4.4 was released today (11/11/2010) and included in the release was an update to the width() and height() methods.z ?Each method will now return the correct dimension of the element if it is within a hidden element. ?For further information, you can view the bug report.

UPDATE #1: Based on the feedback in the comments regarding the use of the swap method, I am updating this post with a solution from Ryan Wheale.? He created a function to return the dimensions of an element that is hidden or nested within 1 more hidden elements.? Here is the code that he posted below in his comment:


//Optional parameter includeMargin is used when calculating outer dimensions
(function($) {
$.fn.getHiddenDimensions = function(includeMargin) {
    var $item = this,
        props = { position: 'absolute', visibility: 'hidden', display: 'block' },
        dim = { width:0, height:0, innerWidth: 0, innerHeight: 0,outerWidth: 0,outerHeight: 0 },
        $hiddenParents = $item.parents().andSelf().not(':visible'),
        includeMargin = (includeMargin == null)? false : includeMargin;

    var oldProps = [];
    $hiddenParents.each(function() {
        var old = {};

        for ( var name in props ) {
            old[ name ] = this.style[ name ];
            this.style[ name ] = props[ name ];
        }

        oldProps.push(old);
    });

    dim.width = $item.width();
    dim.outerWidth = $item.outerWidth(includeMargin);
    dim.innerWidth = $item.innerWidth();
    dim.height = $item.height();
    dim.innerHeight = $item.innerHeight();
    dim.outerHeight = $item.outerHeight(includeMargin);

    $hiddenParents.each(function(i) {
        var old = oldProps[i];
        for ( var name in props ) {
            this.style[ name ] = old[ name ];
        }
    });

    return dim;
}
}(jQuery));


This basically performs the same operations as the swap method.? This is safer to use in case the swap method is removed from the jQuery core.

I have tested this in multiple cases and each time the correct results were returned.

Thanks Ryan.

————————————————————————————————————————————-

Original post

I recently ran into a problem with jQuery’s width(). ?The problem is with a visible element that is inside a hidden element will return a value of 0 instead of its’ actual calculated width. ?After messing around with it for a little bit I finally came up with a solution. ?The method I used involved adding some CSS properties to the hidden element. ?The CSS properties involved are position, visibility, and display.


//$HiddenItem is the element that is wrapping the element that you want the width of
//$Item is the element you want the width of

$HiddenItem.css({
    position: "absolute",
    visibility: "hidden",
    display: "block"
})
$Item.width();

$HiddenItem.css({
    position: "",
    visibility: "",
    display: ""
})

After setting the above CSS properties on the element, you can then call width() and the correct value will be returned. After you call the width() method you should clear the properties in order to return the element to the way it was.

Setting the properties to an empty string is probably not the best way to do it though. What if there was a position value already set? Using this method would clear out that initial values of the CSS properties.

I found the swap() method to be handy in this situation. I found this method while looking through the jQuery source code.

 // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback ) {      var old = {};       // Remember the old values, and insert the new ones      for ( var name in options ) {           old[ name ] = elem.style[ name ];           elem.style[ name ] = options[ name ];      }       callback.call( elem );       // Revert the old values      for ( var name in options ){         ?elem.style[ name ] = old[ name ];      } } 

By using the swap method, the old CSS properties will be remembered and reapplied after finding the width of the element. The swap method takes in 3 parameters:

  1. The element that you would like to swap the CSS properties on
  2. CSS key/value pairs that you want to change
  3. A callback function to call after the properties are set

To rewrite the above to use the swap method I would do the following:


var props = { position: "absolute", visibility: "hidden", display: "block" };
var itemWidth = 0;

$.swap($HiddenItem[0], props, function(){
     itemWidth = $Item.width();
});

//Use itemWidth


I coded up a small example on jsbin. Here is the link http://jsbin.com/ofine3/2.

$HiddenItem.width();