jQuery: Uzyskaj wysokość ukrytego elementu w jQuery

249

Potrzebuję uzyskać wysokość elementu, który jest w div, który jest ukryty. Teraz pokazuję div, zdobywam wysokość i chowam div diva. To wydaje się trochę głupie. Czy jest lepszy sposób?

Używam jQuery 1.4.2:

$select.show();
optionHeight = $firstOption.height(); //we can only get height if its visible
$select.hide();
mkoryak
źródło
42
+1 Twoje przykładowe rozwiązanie jest w rzeczywistości lepsze niż dowolna z odpowiedzi lol.
Tim Santeford
9
Nie zgadzam się z Timem. Dzięki temu rozwiązaniu istnieje możliwość, że wyświetlacz może migotać, ponieważ faktycznie wyświetlasz przedmiot, a następnie go ukrywasz. Chociaż rozwiązanie Nicks jest bardziej skomplikowane, nie ma szans na migotanie wyświetlacza, ponieważ div rodzica nigdy nie jest wyświetlany.
Humphrey,
5
Harry, w rzeczywistości dowiedziałeś się, że twój kod nie działa w IE, nie w tym rozwiązaniu. Debuguj swój kod :)
Gavin

Odpowiedzi:

181

Możesz zrobić coś takiego, trochę zuchwałego, zapomnij, positionczy jest to już absolutne:

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
źródło
17
Skonsolidowałem powyższy kod w wtyczkę jQ jsbin.com/ihakid/2 . Korzystam również z klasy i sprawdzam, czy rodzice nie są również ukryci.
hitautodestruct
3
+1 To działa świetnie. Pamiętaj tylko, że rodzice elementu muszą być również wyświetlani. Przez kilka minut byłem zdezorientowany. Ponadto, jeśli element jest position:relative, oczywiście będziesz chciał ustawić go później zamiast static.
Michael Mior
6
Podczas korzystania z tej metody element docelowy może często zmieniać kształt. Tak więc uzyskanie wysokości i / lub szerokości div, gdy jest ona absolutnie ustawiona, może nie być bardzo przydatna.
Jeremy Ricketts,
2
@Gavin zapobiega to obliczeniom ponownego zalania i migotaniu dla użytkownika, więc jest zarówno tańsze, jak i mniej ingerujące.
Nick Craver
2
@ Hitautodestruct, zmodyfikowałem wtyczkę zapytania, aby sprawdzić, czy tylko element jest faktycznie ukryty. jsbin.com/ihakid/58
Prasad
96

Natknąłem się na ten sam problem z uzyskaniem szerokości ukrytego elementu, więc napisałem to wywołanie wtyczki jQuery Actual, aby to naprawić. Zamiast używać

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

posługiwać się

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

da ci odpowiednią wartość dla ukrytego elementu lub element ma ukrytego rodzica.

Pełna dokumentacja znajduje się tutaj . Na stronie znajduje się również wersja demo.

Mam nadzieję, że to pomoże :)

ben
źródło
3
Tak, to pomaga! 2015 i nadal pomaga. Mówisz „starsze wersje jquery” na stronie Actual - jednak jest to nadal wymagane w v2.1.3, przynajmniej tak było w mojej sytuacji.
LpLrich
Dzięki! Użyłem tej wtyczki do odczytu i ustawienia wysokości elementów (odbywa się to dynamicznie) ukrytych początkowo w harmonii
alds
Niesamowita wtyczka! Próbowałem wszystkiego, aby uzyskać szerokość div wewnątrz modalu, gdy ustawiony na 100% i nic nie działało. ustaw to w 5 sekund i działało idealnie!
Craig Howell,
@ben Link jest uszkodzony.
Sachin Prasad
39

Zmieniasz dwa style CSS, styl wyświetlania i styl widoczności .

Jeśli element jest ukryty poprzez ustawienie stylu css widoczności, powinieneś być w stanie uzyskać wysokość niezależnie od tego, czy element jest widoczny, czy nie, ponieważ element nadal zajmuje miejsce na stronie .

Jeśli element jest ukryty przez zmianę stylu wyświetlania css na „none”, element nie zajmuje miejsca na stronie i trzeba będzie nadać mu styl wyświetlania, który spowoduje, że element będzie renderowany w pewnym miejscu, w który punkt, możesz uzyskać wysokość.

casperOne
źródło
dzięki za to. mój problem zaangażowany komórkę tabeli i ustawienie visibilityna hiddennie rozwiązuje mojego problemu, ale to dało mi pomysł na zestaw position: fixed; top: 100%i działa jak czar!
Jayen
Teraz rozumiem, dlaczego wysokość nie jest wyświetlana z wyświetlaczem: brak elementów. Wielkie dzięki za wspaniałe wyjaśnienia.
FrenkyB
30

Czasami skorzystałem z odrobiny sztuczek, aby sobie z tym poradzić. Opracowałem widżet paska przewijania jQuery, w którym napotkałem problem, którego nie wiem z góry, czy zawartość przewijalna jest częścią ukrytego znacznika, czy nie. Oto co zrobiłem:

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

Działa to w przeważającej części, ale istnieje oczywisty problem, który może potencjalnie się pojawić. Jeśli treść, którą klonujesz, jest stylizowana za pomocą CSS, który zawiera odniesienia do znaczników nadrzędnych w swoich regułach, sklonowana treść nie będzie zawierać odpowiedniego stylu i prawdopodobnie będzie miała nieco inne wymiary. Aby obejść ten problem, możesz upewnić się, że klonowane znaczniki mają zastosowane reguły CSS, które nie zawierają odniesień do znaczników nadrzędnych.

Ponadto nie przyszło mi to z moim widgetem przewijania, ale aby uzyskać odpowiednią wysokość sklonowanego elementu, musisz ustawić szerokość na tę samą szerokość elementu nadrzędnego. W moim przypadku szerokość CSS była zawsze stosowana do rzeczywistego elementu, więc nie musiałem się tym przejmować, jednak jeśli element nie ma zastosowanej szerokości, może być konieczne wykonanie pewnego rodzaju rekurencji przejście przez pochodzenie DOM elementu w celu znalezienia odpowiedniej szerokości elementu nadrzędnego.

elliotlarson
źródło
Ma to jednak interesujący problem. Jeśli element jest już elementem blokowym, klon zajmie całą szerokość ciała (z wyjątkiem wypełnienia ciała lub marginesu), która jest inna niż jego wielkość w kontenerze.
Meligy
16

W oparciu o odpowiedź Nicka i wtyczkę użytkownika hitautodestruct na JSBin stworzyłem podobną wtyczkę jQuery, która pobiera zarówno szerokość, jak i wysokość i zwraca obiekt zawierający te wartości.

Można go znaleźć tutaj: http://jsbin.com/ikogez/3/

Aktualizacja

Całkowicie przeprojektowałem tę małą, niewielką wtyczkę, ponieważ okazało się, że poprzednia wersja (wspomniana powyżej) nie była tak naprawdę użyteczna w rzeczywistych środowiskach, w których odbywało się wiele manipulacji DOM.

Działa to doskonale:

/**
 * 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
źródło
twój kod jest nieprawidłowy. Musisz odnosi się do pierwotnej pozycji, co nie jest widoczne. sizes = {"width": $wrap.width(), "height": $wrap.height()};this
jackJoe
@ jackJoe Używam tego kodu od dłuższego czasu bez problemów. W najlepszym razie powiedziałbym, że potrzebuję $ clone.width () zamiast 'this'. Nie $ wrap, ponieważ chcę rozmiar elementu wewnątrz wrap. Opakowanie może mieć wymiary 10000 x 10000 pikseli, podczas gdy element, który chcę zmierzyć, ma na przykład 30 x 40 pikseli.
Robin van Baalen,
Nawet celowanie $wrapjest w porządku (przynajmniej w moich testach). Próbowałem z thisi oczywiście nie złapałby rozmiaru, ponieważ thiswciąż odnosi się do ukrytego elementu.
jackJoe
1
Dzięki @jackJoe Zaktualizowałem przykład kodu.
Robin van Baalen,
12

Opierając się na odpowiedzi Nicka:

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

Odkryłem, że lepiej to zrobić:

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

Ustawienie atrybutów CSS spowoduje wstawienie ich do tekstu, co spowoduje zastąpienie wszystkich innych atrybutów, które masz w pliku CSS. Po usunięciu atrybutu stylu z elementu HTML wszystko wraca do normy i nadal jest ukryte, ponieważ w pierwszej kolejności zostało ukryte.

Gregory Bolkenstijn
źródło
1
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'}); optionHeight = $("#myDiv").height(); $("#myDiv").css({'position':'','visibility':'', 'display':''});
Aaron
9
... chyba że masz już wbudowane style na elemencie. Tak byłoby w przypadku, gdy ukryłeś element za pomocą jQuery.
Muhd,
4

Możesz także umieścić ukryty div poza ekranem z ujemnym marginesem zamiast przy użyciu display: none, bardzo podobny do techniki zastępowania obrazu za pomocą wcięcia tekstu.

na przykład.

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

W ten sposób wysokość () jest nadal dostępna.

Vaughanos
źródło
3

Próbuję znaleźć działającą funkcję dla ukrytego elementu, ale zdaję sobie sprawę, że CSS jest o wiele bardziej skomplikowany niż wszyscy myślą. Istnieje wiele nowych technik układu w CSS3, które mogą nie działać w przypadku wszystkich poprzednich odpowiedzi, takich jak elastyczne pole, siatka, kolumna lub nawet element wewnątrz złożonego elementu nadrzędnego.

przykład Flexibox wprowadź opis zdjęcia tutaj

Myślę, że jedynym trwałym i prostym rozwiązaniem jest rendering w czasie rzeczywistym . W tym momencie przeglądarka powinna podać ten prawidłowy rozmiar elementu .

Niestety, JavaScript nie zapewnia żadnego bezpośredniego zdarzenia powiadamiającego o wyświetleniu lub ukryciu elementu. Jednak tworzę jakąś funkcję na podstawie interfejsu API zmodyfikowanego atrybutu DOM, który wykona funkcję zwrotną po zmianie widoczności elementu.

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

    // render or adjust something
});

Aby uzyskać więcej informacji, odwiedź mój projekt GitHub.

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

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

Władca dusz
źródło
Dziękuję za odpowiedź. Zainspirowało mnie również do korzystania z MutationObservers. Zauważ jednak, że twoja biblioteka sprawdza tylko zmiany widoczności spowodowane zmianami styleatrybutów (patrz wiersz 59 tutaj ), podczas gdy zmiany widoczności mogą być również spowodowane zmianami classatrybutu.
Ashraf Sabry
Masz rację. Prawidłowa instrukcja powinna brzmieć: e.attributeName! == 'style' && e.attributeName! == 'className'
Soul_Master
2

Zgodnie z rozwiązaniem Nicka Cravera ustawienie widoczności elementu pozwala uzyskać dokładne wymiary. Bardzo często korzystałem z tego rozwiązania. Jednak po ręcznym resetowaniu stylów znalazłem to uciążliwe, biorąc pod uwagę, że modyfikując początkowe pozycjonowanie / wyświetlanie elementu w moim css poprzez programowanie, często zapominam aktualizować powiązany kod javascript. Poniższy kod nie resetuje stylów dla każdego powiedzenia, ale usuwa style wbudowane dodane przez javascript:

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

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

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

Jedynym założeniem jest to, że nie mogą istnieć inne style wbudowane, w przeciwnym razie zostaną one również usunięte. Korzyścią tutaj jest to, że style elementu są zwracane do tego, co były w arkuszu stylów css. W związku z tym możesz zapisać to jako funkcję, przez którą przechodzi element i zwracana jest wysokość lub szerokość.

Innym problemem, jaki znalazłem przy ustawianiu stylów za pomocą js, jest to, że podczas radzenia sobie z przejściami przez css3, jesteś zmuszony dostosować wagi reguł stylu, aby były silniejsze niż styl wbudowany, co może czasami być frustrujące.

Prusprus
źródło
1

Z definicji element ma wysokość tylko wtedy, gdy jest widoczny.

Ciekawe: dlaczego potrzebujesz wysokości ukrytego elementu?

Jedną z możliwości jest skuteczne ukrycie elementu przez umieszczenie go (za pomocą indeksu Z) jakiejś nakładki).

Cletus
źródło
1
potrzebuję wysokości elementu, który zdarza się być ukryty w czasie, gdy chcę jego wysokość.
tworzę
2
innym powodem jest wyśrodkowanie rzeczy za pomocą javascript, które mogą być jeszcze niewidoczne
Simon_Weaver
@cletus Jeśli tylko ciekawi Cię pisanie komentarzy, bardzo często dzieje się tak w przypadku programowania frontendu, aby uzyskać rozmiar ukrytego elementu.
Rantiev
1

W moich okolicznościach miałem również ukryty element, który powstrzymywał mnie przed uzyskaniem wartości wysokości, ale nie był to sam element, ale raczej jeden z jego rodziców ... więc po prostu sprawdziłem, czy jedna z moich wtyczek się sprawdza, czy to jest ukryty, w przeciwnym razie znajdź najbliższy ukryty element. Oto przykład:

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

W rzeczywistości jest to połączenie odpowiedzi Nicka, Gregory'ego i Eyelidlessnessa, aby dać ci zastosowanie ulepszonej metody Gregory'ego, ale wykorzystuje obie metody w przypadku, gdy ma być coś w atrybucie stylu, który chcesz odłożyć, i szuka element nadrzędny.

Moje jedyne zastrzeżenie związane z moim rozwiązaniem polega na tym, że pętla prowadząca przez rodziców nie jest w pełni wydajna.

marksyzm
źródło
1

Jednym z obejść tego problemu jest utworzenie rodzica div poza elementem, dla którego chcesz uzyskać wysokość, zastosowanie wysokości „0” i ukrycie wszelkiego przepełnienia. Następnie weź wysokość elementu potomnego i usuń właściwość przepełnienia elementu nadrzędnego.

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
źródło
0

Oto skrypt, który napisałem, aby obsługiwać wszystkie metody wymiarowania jQuery dla ukrytych elementów, nawet potomków ukrytych rodziców. Zauważ, że z tego powodu jest oczywiście wydajność.

// 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);
brak powiek
źródło
Odpowiedź z braku powiek wygląda idealnie na to, czego potrzebuję, ale nie działa z uruchomionym jQuery: 1.3.2 Powoduje, że jQuery rzuca this.clone (lub coś bardzo blisko) nie jest funkcją. Czy ktoś ma jakieś pomysły, jak to naprawić?
0

Jeśli element był już wcześniej wyświetlany na stronie, możesz po prostu pobrać wysokość bezpośrednio z elementu DOM (osiągalnego w jQuery za pomocą .get (0)), ponieważ jest on ustawiany nawet wtedy, gdy element jest ukryty:

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

to samo dla szerokości:

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

(podziękowania dla Skeets O'Reilly za korektę)

Luca C.
źródło
Powracaundefined
Jake Wilson
1
Jestem pewien, że działa to tylko wtedy, gdy element był już wyświetlany na stronie wcześniej. W przypadku elementów, które zawsze były display='none', to nie zadziała.
Skeets