jQuery: Obtenga la altura del elemento oculto en jQuery

249

Necesito obtener la altura de un elemento que está dentro de un div que está oculto. En este momento muestro el div, obtengo la altura y oculto el div padre. Esto parece un poco tonto. ¿Hay una mejor manera?

Estoy usando jQuery 1.4.2:

$select.show();
optionHeight = $firstOption.height(); //we can only get height if its visible
$select.hide();
mkoryak
fuente
42
+1 Su solución de ejemplo es realmente mejor que cualquiera de las respuestas jajaja.
Tim Santeford
99
No estoy de acuerdo con Tim. Con esta solución, existe la posibilidad de que la pantalla parpadee porque en realidad está mostrando el elemento y luego lo oculta. Aunque la solución de Nicks es más complicada, no tiene posibilidad de parpadear en la pantalla ya que nunca se muestra el div principal.
Humphrey
55
Harry, en realidad lo que descubriste es que tu código no funciona en IE, no esta solución. Vaya a depurar su código :)
Gavin

Respuestas:

181

Podrías hacer algo como esto, aunque un poco hacky, olvida positionsi ya es absoluto:

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 : "");
Nick Craver
fuente
17
Consolidé el código anterior en un complemento jQ jsbin.com/ihakid/2 . También uso una clase y compruebo si los padres también están ocultos.
hitautodestruct
3
+1 Esto funciona muy bien. Solo tenga en cuenta que los padres del elemento también deben mostrarse. Esto me confundió por unos minutos. Además, si el elemento es position:relative, por supuesto, querrá establecer eso después en lugar de static.
Michael Mior
66
Cuando utiliza este método, el elemento objetivo a menudo puede cambiar de forma. Por lo tanto, obtener la altura y / o el ancho del div cuando está en una posición absoluta puede no ser muy útil.
Jeremy Ricketts
2
@Gavin esto evita un cálculo de reflujo y cualquier parpadeo para el usuario, por lo que es más barato y menos intrusivo.
Nick Craver
2
@hitautodestruct, he modificado su complemento de consulta para verificar que solo el elemento esté realmente oculto. jsbin.com/ihakid/58
Prasad
96

Me encontré con el mismo problema al obtener el ancho del elemento oculto, así que escribí este complemento llamado jQuery Actual para solucionarlo. En lugar de usar

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

utilizar

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

le dará el valor correcto para el elemento oculto o el elemento tiene un padre oculto.

Documentación completa por favor ver aquí . También hay una demostración incluida en la página.

Espero que esto ayude :)

ben
fuente
3
Si ayuda! 2015 y todavía ayuda. Usted dice "versiones anteriores de jquery" en el sitio web de Real; sin embargo, todavía se requiere en v2.1.3, al menos en mi situación.
LpLrich
¡Gracias! Usé este complemento para leer y establecer la altura de los elementos (esto se hace dinámicamente) ocultos inicialmente dentro de los acordeones
alds
Plugin impresionante! Intenté todo para obtener el ancho de un div dentro de un modal cuando se configuró al 100% y nada funcionó. ¡configuré esto en 5 segundos y funcionó perfectamente!
Craig Howell
@ben El enlace está roto.
Sachin Prasad
39

Está confundiendo dos estilos CSS, el estilo de visualización y el estilo de visibilidad .

Si el elemento está oculto configurando el estilo CSS de visibilidad, entonces debería poder obtener la altura independientemente de si el elemento es visible o no, ya que el elemento todavía ocupa espacio en la página .

Si el elemento se oculta cambiando el estilo de visualización de CSS a "ninguno", entonces el elemento no ocupa espacio en la página, y tendrá que darle un estilo de visualización que hará que el elemento se procese en algún espacio, en en qué punto, puedes obtener la altura.

casperOne
fuente
gracias por esto. mi problema implicó una celda de la tabla, y el establecimiento visibilityde hiddenno resolvió mi problema, pero me dio la idea de conjunto position: fixed; top: 100%y funciona como un encanto!
Jayen
Ahora entiendo por qué su altura no se muestra con la pantalla: ninguno de los elementos. Muchas gracias por la maravillosa explicación.
FrenkyB
30

De hecho, he recurrido a algunos trucos para lidiar con esto a veces. Desarrollé un widget de barra de desplazamiento jQuery donde encontré el problema que no sé de antemano si el contenido desplazable es parte de un marcado oculto o no. Esto es lo que hice:

// 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();
}

Esto funciona en su mayor parte, pero hay un problema obvio que potencialmente puede surgir. Si el contenido que está clonando tiene un estilo CSS que incluye referencias al marcado principal en sus reglas, el contenido clonado no contendrá el estilo apropiado y probablemente tendrá medidas ligeramente diferentes. Para evitar esto, puede asegurarse de que el marcado que está clonando tenga aplicadas reglas CSS que no incluyan referencias al marcado primario.

Además, esto no se me ocurrió con mi widget de desplazamiento, pero para obtener la altura adecuada del elemento clonado, deberá establecer el ancho al mismo ancho del elemento principal. En mi caso, siempre se aplicó un ancho CSS al elemento real, por lo que no tuve que preocuparme por esto, sin embargo, si el elemento no tiene un ancho aplicado, es posible que deba hacer algún tipo de recursivo recorrido de la ascendencia DOM del elemento para encontrar el ancho del elemento padre apropiado.

elliotlarson
fuente
Sin embargo, esto tiene un problema interesante. Si el elemento ya es un elemento de bloque, el clon tomará todo el ancho del cuerpo (excepto el relleno o el margen del cuerpo), que es diferente de su tamaño dentro de un contenedor.
Meligy
16

Continuando con la respuesta del usuario Nick y el complemento de usuario hitautodestruct en JSBin, he creado un complemento similar de jQuery que recupera ancho y alto y devuelve un objeto que contiene estos valores.

Se puede encontrar aquí: http://jsbin.com/ikogez/3/

Actualizar

Rediseñé por completo este pequeño plugin, ya que resultó que la versión anterior (mencionada anteriormente) no era realmente utilizable en entornos de la vida real donde estaba ocurriendo una gran cantidad de manipulación DOM.

Esto funciona perfectamente:

/**
 * 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 <[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;
};
Robin van Baalen
fuente
Su código no es correcto. Necesita sizes = {"width": $wrap.width(), "height": $wrap.height()};las thisreferencias al elemento original, que no está visible.
jackJoe
@jackJoe He estado usando este código durante bastante tiempo sin problemas. En el mejor de los casos, diría que necesito $ clone.width () en lugar de 'esto'. No $ wrap ya que quiero el tamaño del elemento dentro del wrap. Wrapper podría tener 10000x10000px, mientras que el elemento que quiero medir sigue siendo 30x40px, por ejemplo.
Robin van Baalen
Incluso apuntar al $wrapestá bien (al menos en mis pruebas). Intenté con el thisy obviamente no alcanzaría el tamaño porque thistodavía se refiere al elemento oculto.
jackJoe
1
Gracias @jackJoe ahora he actualizado el ejemplo de código.
Robin van Baalen
12

Sobre la base de la respuesta de Nick:

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

Descubrí que es mejor hacer esto:

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

La configuración de los atributos CSS los insertará en línea, lo que sobrescribirá cualquier otro atributo que tenga en su archivo CSS. Al eliminar el atributo de estilo en el elemento HTML, todo vuelve a la normalidad y aún está oculto, ya que estaba oculto en primer lugar.

Gregory Bolkenstijn
fuente
1
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'}); optionHeight = $("#myDiv").height(); $("#myDiv").css({'position':'','visibility':'', 'display':''});
Aaron
99
... a menos que ya tenga estilos en línea en el elemento. Cuál sería el caso si ocultaras el elemento con jQuery.
Muhd
4

También puede colocar el div oculto fuera de la pantalla con un margen negativo en lugar de usar la pantalla: ninguno, al igual que la técnica de reemplazo de imagen de sangría de texto.

p.ej.

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

De esta manera, la altura () todavía está disponible.

vaughanos
fuente
3

Intento encontrar una función de trabajo para el elemento oculto, pero me doy cuenta de que CSS es mucho más complejo de lo que todos piensan. Hay muchas nuevas técnicas de diseño en CSS3 que podrían no funcionar para todas las respuestas anteriores, como el cuadro flexible, la cuadrícula, la columna o incluso el elemento dentro del elemento padre complejo.

ejemplo de flexibox ingrese la descripción de la imagen aquí

Creo que la única solución sostenible y simple es el renderizado en tiempo real . En ese momento, el navegador debería darle el tamaño de elemento correcto .

Lamentablemente, JavaScript no proporciona ningún evento directo para notificar cuando el elemento se muestra u oculta. Sin embargo, creo alguna función basada en la API de atributo modificado de DOM que ejecutará la función de devolución de llamada cuando se cambie la visibilidad del elemento.

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

    // render or adjust something
});

Para obtener más información, visite mi proyecto GitHub.

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

demostración: http://jsbin.com/ETiGIre/7

Soul_Master
fuente
Gracias por tu respuesta. También me inspiró a usar MutationObservers. Pero tenga en cuenta que su biblioteca solo verifica los cambios de visibilidad causados ​​por los stylecambios de atributos (consulte la línea 59 aquí ), mientras que los cambios de visibilidad también pueden ser causados ​​por cambios en el classatributo.
Ashraf Sabry
Tienes razón. La declaración correcta debe ser e.attributeName! == 'style' && e.attributeName! == 'className'
Soul_Master
2

Siguiendo la solución de Nick Craver, establecer la visibilidad del elemento le permite obtener dimensiones precisas. He usado esta solución muy muy a menudo. Sin embargo, teniendo que restablecer los estilos manualmente, he llegado a encontrar esto engorroso, dado que al modificar el posicionamiento / visualización inicial del elemento en mi CSS a través del desarrollo, a menudo olvido actualizar el código JavaScript relacionado. El siguiente código no restablece los estilos por ejemplo, pero elimina los estilos en línea agregados por javascript:

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

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

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

La única suposición aquí es que no puede haber otros estilos en línea o de lo contrario se eliminarán también. Sin embargo, el beneficio aquí es que los estilos del elemento se devuelven a lo que eran en la hoja de estilo CSS. Como consecuencia, puede escribir esto como una función por la que se pasa un elemento y se devuelve un alto o ancho.

Otro problema que he encontrado al configurar los estilos en línea a través de js es que cuando se trata de transiciones a través de css3, te ves obligado a adaptar los pesos de tus reglas de estilo para que sean más fuertes que un estilo en línea, lo que a veces puede ser frustrante.

Prusprus
fuente
1

Por definición, un elemento solo tiene altura si es visible.

Simplemente curioso: ¿por qué necesitas la altura de un elemento oculto?

Una alternativa es ocultar eficazmente un elemento poniéndolo detrás (usando el índice z) una superposición de algún tipo).

cletus
fuente
1
Necesito la altura de un elemento, que está oculto durante el tiempo que quiero su altura. Estoy creando un complemento y necesito recopilar algunos datos a medida que lo inicio
mkoryak
2
otra razón es para centrar cosas con javascript que pueden no ser visibles todavía
Simon_Weaver
@cletus Si solo tiene curiosidad por escribir comentarios, esa es una situación muy frecuente en la codificación de la interfaz para obtener el tamaño de un elemento oculto.
Rantiev
1

En mi caso, también tenía un elemento oculto que me impedía obtener el valor de la altura, pero no era el elemento en sí, sino uno de sus padres ... así que compruebo uno de mis complementos para ver si es oculto, de lo contrario, encuentre el elemento oculto más cercano. Aquí hay un ejemplo:

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');
    }
}

De hecho, esta es una amalgama de las respuestas de Nick, Gregory y Eyelidlessness para darle el uso del método mejorado de Gregory, pero utiliza ambos métodos en caso de que se suponga que hay algo en el atributo de estilo que desea volver a colocar, y busca un elemento padre

Mi única queja con mi solución es que el ciclo a través de los padres no es del todo eficiente.

marksyzm
fuente
1

Una solución alternativa es crear un div padre fuera del elemento del que desea obtener la altura, aplicar una altura de '0' y ocultar cualquier desbordamiento. A continuación, tome la altura del elemento secundario y elimine la propiedad de desbordamiento del elemento primario.

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>

Mate
fuente
0

Aquí hay un script que escribí para manejar todos los métodos de dimensión de jQuery para elementos ocultos, incluso descendientes de padres ocultos. Tenga en cuenta que, por supuesto, hay un éxito en el rendimiento al usar esto.

// 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);
párpado
fuente
La respuesta de eyelidlessness se ve perfecta para lo que necesito pero no funciona con el jQuery que estoy ejecutando: 1.3.2 Hace que jQuery arroje un this.clone (o algo muy cercano) no es una función. ¿Alguien tiene alguna idea sobre cómo solucionarlo?
0

Si ya ha mostrado el elemento en la página anteriormente, simplemente puede tomar la altura directamente del elemento DOM (accesible en jQuery con .get (0)), ya que está configurado incluso cuando el elemento está oculto:

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

lo mismo para el ancho:

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

(gracias a Skeets O'Reilly por la corrección)

Luca C.
fuente
Devolucionesundefined
Jake Wilson
1
Estoy bastante seguro de que esto solo funciona si ya ha mostrado el elemento en la página anteriormente. Para elementos que siempre han sido display='none', esto no funcionará.
Skeets