jQuery: récupère la hauteur de l'élément caché dans jQuery

249

J'ai besoin d'obtenir la hauteur d'un élément qui se trouve dans un div qui est caché. En ce moment, je montre le div, obtient la hauteur et cache le div parent. Cela semble un peu idiot. Y a-t-il une meilleure façon?

J'utilise jQuery 1.4.2:

$select.show();
optionHeight = $firstOption.height(); //we can only get height if its visible
$select.hide();
mkoryak
la source
42
+1 Votre exemple de solution est en fait meilleur que n'importe laquelle des réponses lol.
Tim Santeford
9
Je suis en désaccord avec Tim. Avec cette solution, il est possible que l'affichage scintille parce que vous affichez réellement l'élément, puis le cachez. Même si la solution Nicks est plus compliquée, elle n'a aucune chance de scintiller l'affichage car le div parent n'est jamais affiché.
Humphrey
5
Harry, en fait, ce que vous avez découvert, c'est que votre code ne fonctionne pas dans IE, pas cette solution. Allez déboguer votre code :)
Gavin

Réponses:

181

Vous pourriez faire quelque chose comme ça, un peu hacky cependant, oubliez positionsi c'est déjà absolu:

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
la source
17
J'ai consolidé le code ci-dessus dans un plugin jQ jsbin.com/ihakid/2 . J'utilise également une classe et vérifie si les parents sont également masqués.
hitautodestruct
3
+1 Cela fonctionne très bien. N'oubliez pas que les parents de l'élément doivent également être affichés. Cela m'a confus pendant quelques minutes. De plus, si l'élément est position:relative, vous voudrez bien sûr le définir après au lieu de static.
Michael Mior
6
Lorsque vous utilisez cette méthode, l'élément cible peut souvent changer de forme. Ainsi, obtenir la hauteur et / ou la largeur du div lorsqu'il est absolument positionné peut ne pas être très utile.
Jeremy Ricketts
2
@Gavin, cela empêche un calcul de refusion et tout scintillement pour l'utilisateur, c'est donc moins cher et moins intrusif.
Nick Craver
2
@hitautodestruct, j'ai modifié votre plugin de requête pour vérifier que seul l'élément est réellement caché. jsbin.com/ihakid/58
Prasad
96

J'ai rencontré le même problème avec l'obtention de la largeur d'élément caché, j'ai donc écrit ce plugin appelé jQuery Actual pour le corriger. À la place d'utiliser

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

utilisation

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

vous donnera la bonne valeur pour l'élément caché ou l'élément a un parent caché.

Veuillez consulter la documentation complète ici . Il y a aussi une démo incluse dans la page.

J'espère que cette aide :)

ben
la source
3
Oui ça aide! 2015 et ça aide toujours. Vous dites "anciennes versions de jquery" sur le site Web Actual - cependant - il est toujours requis dans la v2.1.3, du moins c'était dans ma situation.
LpLrich
Merci! J'ai utilisé ce plugin pour lire et définir la hauteur des éléments (cela se fait dynamiquement) cachés initialement dans les accordéons
alds
Plugin génial! J'ai tout essayé pour obtenir la largeur d'un div à l'intérieur d'un modal lorsqu'il est réglé à 100% et rien n'a fonctionné. mis en place en 5 secondes et a parfaitement fonctionné!
Craig Howell
@ben Le lien est rompu.
Sachin Prasad
39

Vous confondez deux styles CSS, le style d'affichage et le style de visibilité .

Si l'élément est masqué en définissant le style CSS de visibilité, vous devriez pouvoir obtenir la hauteur, qu'il soit visible ou non, car l'élément prend toujours de la place sur la page .

Si l'élément est masqué en changeant le style d'affichage CSS en "aucun", alors l'élément ne prend pas d'espace sur la page, et vous devrez lui donner un style d'affichage qui provoquera le rendu de l'élément dans un certain espace, à quel point, vous pouvez obtenir la hauteur.

casperOne
la source
Merci pour cela. mon problème impliquait une cellule de tableau, et le réglage visibilitypour hiddenne pas résoudre mon problème, mais cela m'a donné l'idée de définir position: fixed; top: 100%et cela fonctionne comme un charme!
Jayen
Maintenant, je comprends pourquoi sa hauteur n'est pas affichée avec affichage: aucun élément. Merci beaucoup pour cette merveilleuse explication.
FrenkyB
30

J'ai eu recours à un peu de ruse pour y faire face à certains moments. J'ai développé un widget jQuery scrollbar où j'ai rencontré le problème que je ne sais pas à l'avance si le contenu scrollable fait partie d'un balisage caché ou non. Voici ce que j'ai fait:

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

Cela fonctionne pour la plupart, mais il y a un problème évident qui peut potentiellement se poser. Si le contenu que vous clonez est stylisé avec CSS qui inclut des références au balisage parent dans leurs règles, le contenu cloné ne contiendra pas le style approprié et aura probablement des mesures légèrement différentes. Pour contourner ce problème, vous pouvez vous assurer que le balisage que vous clonez a des règles CSS qui ne contiennent pas de références au balisage parent.

En outre, cela ne m'est pas venu avec mon widget de défilement, mais pour obtenir la hauteur appropriée de l'élément cloné, vous devrez définir la largeur à la même largeur de l'élément parent. Dans mon cas, une largeur CSS a toujours été appliquée à l'élément réel, donc je n'ai pas eu à m'en soucier, cependant, si l'élément n'a pas de largeur appliqué, vous devrez peut-être faire une sorte de récursivité traversée de l'ascendance DOM de l'élément pour trouver la largeur de l'élément parent approprié.

elliotlarson
la source
Cela a cependant un problème intéressant. Si l'élément est déjà un élément de bloc, le clone prendra toute la largeur du corps (sauf le rembourrage ou la marge du corps), ce qui est différent de sa taille à l'intérieur d'un conteneur.
Meligy
16

En m'appuyant sur la réponse de l'utilisateur Nick et sur le plugin de hitautodestruct de l'utilisateur sur JSBin, j'ai créé un plugin jQuery similaire qui récupère à la fois la largeur et la hauteur et renvoie un objet contenant ces valeurs.

Il peut être trouvé ici: http://jsbin.com/ikogez/3/

Mettre à jour

J'ai complètement repensé ce minuscule petit plugin car il s'est avéré que la version précédente (mentionnée ci-dessus) n'était pas vraiment utilisable dans des environnements réels où de nombreuses manipulations DOM se produisaient.

Cela fonctionne parfaitement:

/**
 * 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
la source
votre code n'est pas correct. Vous avez besoin sizes = {"width": $wrap.width(), "height": $wrap.height()};du thisfait référence à l'article d'origine, qui n'est pas visible.
jackJoe
@jackJoe J'utilise ce code depuis un certain temps sans problème. Au mieux, je dirais que j'ai besoin de $ clone.width () au lieu de 'this'. Pas $ wrap car je veux la taille de l'élément à l'intérieur du wrap. Wrapper pourrait être 10000x10000px tandis que l'élément que je veux mesurer est toujours 30x40px par exemple.
Robin van Baalen
Même le ciblage $wrapest correct (au moins dans mes tests). J'ai essayé avec le thiset évidemment il n'attraperait pas la taille car thisil fait toujours référence à l'élément caché.
jackJoe
1
Merci @jackJoe J'ai maintenant mis à jour l'exemple de code.
Robin van Baalen
12

S'appuyant sur la réponse de Nick:

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

J'ai trouvé qu'il valait mieux faire ça:

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

La définition des attributs CSS les insérera en ligne, ce qui remplacera tous les autres attributs que vous avez dans votre fichier CSS. En supprimant l'attribut style sur l'élément HTML, tout est revenu à la normale et toujours caché, car il était caché en premier lieu.

Gregory Bolkenstijn
la source
1
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'}); optionHeight = $("#myDiv").height(); $("#myDiv").css({'position':'','visibility':'', 'display':''});
Aaron
9
... sauf si vous disposiez déjà de styles en ligne sur l'élément. Ce serait le cas si vous cachiez l'élément avec jQuery.
Muhd
4

Vous pouvez également positionner la div cachée hors de l'écran avec une marge négative plutôt que d'utiliser l'affichage: aucun, un peu comme une technique de remplacement d'image avec retrait de texte.

par exemple.

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

De cette façon, la hauteur () est toujours disponible.

vaughanos
la source
3

J'essaie de trouver une fonction de travail pour l'élément caché mais je me rends compte que CSS est beaucoup plus complexe que tout le monde ne le pense. Il y a beaucoup de nouvelles techniques de mise en page dans CSS3 qui pourraient ne pas fonctionner pour toutes les réponses précédentes comme la boîte flexible, la grille, la colonne ou même l'élément à l'intérieur d'un élément parent complexe.

exemple de flexibox entrez la description de l'image ici

Je pense que la seule solution durable et simple est le rendu en temps réel . À ce moment-là, le navigateur devrait vous donner la bonne taille d'élément .

Malheureusement, JavaScript ne fournit aucun événement direct pour avertir lorsque l'élément est affiché ou masqué. Cependant, je crée une fonction basée sur l'API DOM Attribute Modified qui exécutera la fonction de rappel lorsque la visibilité de l'élément est modifiée.

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

    // render or adjust something
});

Pour plus d'informations, veuillez visiter mon projet GitHub.

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

démo: http://jsbin.com/ETiGIre/7

Maître de l'âme
la source
Merci pour votre réponse. Cela m'a inspiré d'utiliser également MutationObservers. Mais notez que votre bibliothèque vérifie uniquement les changements de visibilité causés par les stylechangements d'attribut (voir la ligne 59 ici ), tandis que les changements de visibilité peuvent également être causés par des changements dans l' classattribut.
Ashraf Sabry
Vous avez raison. L'instruction correcte doit être e.attributeName! == 'style' && e.attributeName! == 'className'
Soul_Master
2

Suivant la solution de Nick Craver, le réglage de la visibilité de l'élément lui permet d'obtenir des dimensions précises. J'ai utilisé cette solution très souvent. Cependant, devant réinitialiser les styles manuellement, je suis arrivé à trouver cela lourd, étant donné que la modification du positionnement / affichage initial de l'élément dans mon CSS au cours du développement, j'oublie souvent de mettre à jour le code javascript associé. Le code suivant ne réinitialise pas les styles par exemple, mais supprime les styles en ligne ajoutés par javascript:

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

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

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

La seule hypothèse ici est qu'il ne peut pas y avoir d'autres styles en ligne, sinon ils seront également supprimés. L'avantage ici, cependant, est que les styles de l'élément reviennent à ce qu'ils étaient dans la feuille de style CSS. Par conséquent, vous pouvez l'écrire comme une fonction où un élément est traversé et une hauteur ou une largeur est renvoyée.

Un autre problème que j'ai trouvé en définissant les styles en ligne via js est que lorsque vous traitez avec des transitions via css3, vous êtes obligé d'adapter les poids de vos règles de style pour être plus forts qu'un style en ligne, ce qui peut parfois être frustrant.

Prusprus
la source
1

Par définition, un élément n'a de hauteur que s'il est visible.

Juste curieux: pourquoi avez-vous besoin de la hauteur d'un élément caché?

Une alternative consiste à masquer efficacement un élément en le plaçant derrière (en utilisant z-index) une superposition quelconque).

cletus
la source
1
j'ai besoin d'une hauteur d'un élément, qui se trouve être cachée pendant le temps que je veux sa hauteur. je suis en
train de
2
une autre raison est de centrer des choses avec javascript qui ne sont pas encore visibles
Simon_Weaver
@cletus Si vous êtes juste curieux d'écrire des commentaires, c'est très souvent une situation dans le codage frontal pour obtenir la taille d'un élément caché.
Rantiev
1

Dans ma circonstance, j'avais également un élément caché qui m'empêchait d'obtenir la valeur de la hauteur, mais ce n'était pas l'élément lui-même mais plutôt l'un de ses parents ... alors j'ai simplement vérifié un de mes plugins pour voir si c'était caché, sinon trouver l'élément caché le plus proche. Voici un exemple:

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

En fait, il s'agit d'une fusion des réponses de Nick, Gregory et Eyelidlessness pour vous donner l'utilisation de la méthode améliorée de Gregory, mais utilise les deux méthodes au cas où il est censé y avoir quelque chose dans l'attribut de style que vous souhaitez remettre, et cherche un élément parent.

Mon seul reproche avec ma solution est que la boucle à travers les parents n'est pas entièrement efficace.

marksyzm
la source
1

Une solution consiste à créer un div parent en dehors de l'élément dont vous souhaitez obtenir la hauteur, à appliquer une hauteur de «0» et à masquer tout débordement. Ensuite, prenez la hauteur de l'élément enfant et supprimez la propriété de débordement du 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>

Mat
la source
0

Voici un script que j'ai écrit pour gérer toutes les méthodes de dimension de jQuery pour les éléments cachés, même les descendants de parents cachés. Notez que, bien sûr, cela a un impact sur les performances.

// 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);
sans paupières
la source
La réponse par eyelidlessness semble parfaite pour ce dont j'ai besoin mais ne fonctionne pas avec le jQuery que j'utilise: 1.3.2 Il fait que jQuery lance un this.clone (ou quelque chose de très proche) n'est pas une fonction. Quelqu'un a-t-il des idées sur la façon de le réparer?
0

Si vous avez déjà affiché l'élément sur la page précédemment, vous pouvez simplement prendre la hauteur directement à partir de l'élément DOM (accessible en jQuery avec .get (0)), car il est défini même lorsque l'élément est masqué:

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

idem pour la largeur:

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

(merci à Skeets O'Reilly pour la correction)

Luca C.
la source
Retoursundefined
Jake Wilson
1
Je suis sûr que cela ne fonctionne que si vous avez déjà affiché l'élément sur la page auparavant. Pour les éléments qui l'ont toujours été display='none', cela ne fonctionnera pas.
Skeets