jQuery - Görünmediğinde Öğenin Genişliğini Al (Görüntüleme: Yok)


109

Bir eleman görünür olmadığında jQuery'deki gibi görünüyor width () 0 döndürür. Mantıklı, ancak üst öğeyi göstermeden önce üst öğenin genişliğini ayarlamak için bir tablonun genişliğini almam gerekiyor.

Aşağıda belirtildiği gibi, üstte, ebeveynin eğrilmesine ve kötü görünmesine neden olan bir metin vardır. Ebeveynin yalnızca tablo kadar geniş olmasını ve metin kaydırmasına sahip olmasını istiyorum.

<div id="parent">
    Text here ... Can get very long and skew the parent
    <table> ... </table>
    Text here too ... which is why I want to shrink the parent based on the table
</div>

CSS:

#parent
{
    display: none;
}

JavaScript:

var tableWidth = $('#parent').children('table').outerWidth();
if (tableWidth > $('#parent').width())
{
    $('#parent').width(tableWidth);
}

tableWidth görünmediği için her zaman 0 döndürür (göründüğünde bana bir sayı verdiği için benim tahminimdir). Ebeveyni görünür hale getirmeden tablonun genişliğini almanın bir yolu var mı?

Yanıtlar:


95

İşte kullandığım bir numara. JQuery'nin öğenin görünür olduğunu düşünmesini sağlamak için bazı CSS özelliklerinin eklenmesini içerir, ancak aslında hala gizlidir.

var $table = $("#parent").children("table");
$table.css({ position: "absolute", visibility: "hidden", display: "block" });
var tableWidth = $table.outerWidth();
$table.css({ position: "", visibility: "", display: "" });

Bu bir çeşit hack, ama benim için iyi çalışıyor gibi görünüyor.

GÜNCELLEME

O zamandan beri bu konuyu kapsayan bir blog yazısı yazdım . Yukarıda kullanılan yöntem, CSS özelliklerini boş değerlere sıfırladığınız için sorunlu olma potansiyeline sahiptir. Ya önceden değerleri varsa? Güncellenen çözüm, jQuery kaynak kodunda bulunan swap () yöntemini kullanır.

Referans alınan blog gönderisindeki kod:

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

1
Bu, tarayıcının sayfayı iki kez yeniden çizmesine neden olmaz mı? Eğer öyleyse, bu yetersiz bir çözümdür.
marcusklaas

@Tim Banks çok güzel! Aslında benzer bir uzantı yazdım. Farkına vardığım şey Firefox'ta çok garip bir davranış , width () hem sizin hem de eklentim için 0 döndürüyor. Üstelik hiçbir zaman doldurmayı hesaba katmaz. jsfiddle.net/67cgB . Bunu düzeltmek için başka ne yapabileceğimi bulmakta çok zorlanıyorum.
Mark Pieszak - Trilon.io

Gizli akordeon boyutlarını elde etmek için jquery akordeon içindeki bu hack'i nasıl kullanabilirim?
Deepak Ingole

1
@FercoCQ Referans alınan blog gönderisindeki kodu ekledim.
Tim Banks


41
function realWidth(obj){
    var clone = obj.clone();
    clone.css("visibility","hidden");
    $('body').append(clone);
    var width = clone.outerWidth();
    clone.remove();
    return width;
}
realWidth($("#parent").find("table:first"));

kirli güzel numara !!! Klonun doğru css özelliklerine sahip olduğundan emin olun (ör. Orijinal öğenin yazı tipi boyutu 15 ise, clone.css ("görünürlük", "gizli") kullanmalısınız. Css ("yazı tipi boyutu", "15 piksel" );)
alex

18
Sadece belirtmek gerekirse, öğeniz genişliğini herhangi bir ebeveynden devraldıysa bu işe yaramaz. Sadece yanlış olan vücut genişliğini devralacaktır.
Dekan

3
Ben modifiye clone.css("visibility","hidden");etmek clone.css({"visibility":"hidden", "display":"table"});hangi konu @Dean tarafından işaret giderir
isapir

Çok teşekkürler! Durumum için küçük değişikliklerle çalıştı: Klonlanacak bir nesneyi ve gerçek genişliğini elde etmek için içindeki bir seçiciyi destekleyecek şekilde değiştirdim. Her zaman gizli olan aynı kök nesneyi ölçmek istemeyiz.
falsarella

Ben de aynı yaklaşımı kullanırdım ve o gün pek çok olumsuz oy aldım. Çok fazla olumlu oyunuz olması garip: D Size bunun neden kötü bir çözüm olduğunu ve birkaç yıl önce cevabımın altında neyin işaret edildiğini anlatacağım. Öğeyi kopyalayıp doğrudan gövdeye koyduğunuzda, büyük olasılıkla bu belirli öğeyle ilgili tüm CSS'yi kaldırıyorsunuz. Örneğin. Bu from: #some wrapper .hidden_element{/*Some CSS*/}Bu yapıyoruz: body .hidden_element. Artık #some_wrapper .hidden_elementkullanılmıyor! Sonuç olarak, hesaplanan bedeniniz orijinal boyutundan farklı olabilir. TLTR: Sadece stilleri kaybediyorsunuz
bunun yerine

8

Roberts cevabına göre, işte benim işlevim. Bu, öğe veya üst öğesi jQuery aracılığıyla soluklaştıysa, iç veya dış boyutları alabilir ve ayrıca ofset değerlerini döndürürse işe yarar.

/ edit1: işlevi yeniden yazdı. artık daha küçük ve doğrudan nesne üzerinden çağrılabilir

/ edit2: işlev artık klonu gövde yerine orijinal öğenin hemen sonrasına ekleyerek klonun miras alınan boyutları korumasını mümkün kılar.

$.fn.getRealDimensions = function (outer) {
    var $this = $(this);
    if ($this.length == 0) {
        return false;
    }
    var $clone = $this.clone()
        .show()
        .css('visibility','hidden')
        .insertAfter($this);        
    var result = {
        width:      (outer) ? $clone.outerWidth() : $clone.innerWidth(), 
        height:     (outer) ? $clone.outerHeight() : $clone.innerHeight(), 
        offsetTop:  $clone.offset().top, 
        offsetLeft: $clone.offset().left
    };
    $clone.remove();
    return result;
}

var dimensions = $('.hidden').getRealDimensions();

İhtiyaç duyanlar için YUI sürümü: gist.github.com/samueljseay/13c2e69508563b2f0458
Kod Novitiate

OffsetTop 'gerçek' öğe değil klon olduğu göz önüne alındığında öğe için doğru mu? $ This.offset (). Top kullanmalı mısınız? Ayrıca, üst / solu ne için kullandığınızı merak ediyor musunuz? Sadece 'genişlik' kullanıyorum ama bir sebepten ötürü bu ofsetlerle ilgilenmem gerekip gerekmediğini merak ediyorum.
Terry

3

Yukarıdaki realWidth işlevini gönderdiğiniz için teşekkür ederiz, bana gerçekten yardımcı oldu. Yukarıdaki "realWidth" işlevine dayanarak, bir CSS sıfırlama yazdım (neden aşağıda açıklanmıştır).

function getUnvisibleDimensions(obj) {
    if ($(obj).length == 0) {
        return false;
    }

    var clone = obj.clone();
    clone.css({
        visibility:'hidden',
        width : '',
        height: '',
        maxWidth : '',
        maxHeight: ''
    });
    $('body').append(clone);
    var width = clone.outerWidth(),
        height = clone.outerHeight();
    clone.remove();
    return {w:width, h:height};
}

"realWidth", mevcut bir etiketin genişliğini alır. Bunu bazı resim etiketleriyle test ettim. Sorun şu ki, görüntü genişlik başına CSS boyutu (veya maksimum genişlik) verdiğinde, o görüntünün gerçek boyutunu asla elde edemezsiniz. Belki de img "max-width: 100%" e sahiptir, "realWidth" işlevi onu klonlar ve vücuda ekler. Görüntünün orijinal boyutu gövdeden daha büyükse, o görüntünün gerçek boyutunu değil, vücudun boyutunu elde edersiniz.


3

Gizli öğe için çalışma işlevi bulmaya çalışıyorum ama CSS'nin herkesin düşündüğünden çok daha karmaşık olduğunu anlıyorum. CSS3'te esnek kutu, ızgara, sütun ve hatta karmaşık ana öğenin içindeki öğeler gibi önceki yanıtların tümü için işe yaramayabilecek birçok yeni düzen tekniği vardır.

flexibox örneği görüntü açıklamasını buraya girin

Bence tek sürdürülebilir ve basit çözüm gerçek zamanlı renderleme . O zaman, tarayıcı size doğru eleman boyutunu vermelidir .

Ne yazık ki JavaScript, öğe gösterildiğinde veya gizlendiğinde bildirimde bulunmak için herhangi bir doğrudan olay sağlamaz. Ancak, öğenin görünürlüğü değiştiğinde geri arama işlevini yürütecek DOM Özniteliği Değiştirilmiş API'ye dayalı bazı işlevler oluşturuyorum.

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

    // render or adjust something
});

Daha fazla bilgi için lütfen GitHub projemi ziyaret edin.

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

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


4
CSS herkesin sandığından çok daha karmaşık Bu çok doğru…
dgellow

3

Gizli olan bir şeyin genişliğine ihtiyacınız varsa ve herhangi bir nedenle onu gizleyemiyorsanız, onu klonlayabilir, CSS'yi sayfa dışında görünecek şekilde değiştirebilir, görünmez hale getirebilir ve ardından ölçebilirsiniz. Kullanıcı, gizlenirse ve daha sonra silinirse daha akıllıca olmayacaktır.

Buradaki diğer cevaplardan bazıları sadece görünürlüğü gizleyerek işe yarıyor, ancak sayfanızda bir saniyeden kısa bir süre için boş bir yer kaplayacak.

Misal:

$itemClone = $('.hidden-item').clone().css({
        'visibility': 'hidden',
        'position': 'absolute',
        'z-index': '-99999',
        'left': '99999999px',
        'top': '0px'
    }).appendTo('body');
var width = $itemClone.width();
$itemClone.remove();

2
Öğe boyutları, mizanpaj hiyerarşisine bağlı olarak bir üst kapsayıcıdan veya daha karmaşık bir CSS seçiciden etkilenirse çalışmaz.
Sebastian Nowak

2

Genişliği almadan önce ana ekranı gösterin, ardından genişliği alın ve son olarak ana ekranı gizleyin. Tıpkı takip etmek gibi

$('#parent').show();
var tableWidth = $('#parent').children('table').outerWidth();
 $('#parent').hide();
if (tableWidth > $('#parent').width())
{
    $('#parent').width() = tableWidth;
}

2

Her durumda işe yaramayacak olsa da bir çözüm, opaklığı 0'a ayarlayarak öğeyi gizlemektir. Tamamen şeffaf bir öğe genişliğe sahip olacaktır.

Geri çekilme, öğenin yine de yer kaplamasıdır, ancak bu her durumda bir sorun olmayacaktır.

Örneğin:

$(img).css("opacity", 0)  //element cannot be seen
width = $(img).width()    //but has width

1

Buradaki çoğu çözümün kaçırdığı en büyük sorun, bir öğenin genişliğinin genellikle html'de kapsama alanına bağlı olarak CSS tarafından değiştirilmesidir.

Bir elemanın offsetWidth'ini, yalnızca mevcut kapsamı için geçerli olan stillere sahipken gövdeye bir klonunu ekleyerek belirleyecek olsaydım, yanlış genişliği elde ederdim.

Örneğin:

// css

.module-container .my-elem {border: 60px düz siyah; }

şimdi beden bağlamında-elemamın genişliğini belirlemeye çalıştığımda 120px çıkacak. Bunun yerine modül kabını klonlayabilirsiniz, ancak JS'nizin bu ayrıntıları bilmesi gerekmez.

Test etmedim ama esasen Soul_Master'ın çözümü düzgün çalışabilen tek çözüm gibi görünüyor. Ancak maalesef uygulamaya bakıldığında, muhtemelen kullanımı maliyetli ve performans açısından kötü olacaktır (burada sunulan çözümlerin çoğu da öyle.)

Mümkünse bunun yerine visibility: hidden'ı kullanın. Bu, tüm karışıklık olmadan genişliği hesaplamanıza izin vererek, öğeyi yine de oluşturacaktır.


0

Tim Banks tarafından gönderilen cevaba ek olarak çözmek için basit bir şeyi düzenlemem gerekiyordu.

Bir başkası da aynı sorunu yaşayabilir; Bir açılır menüde bir Bootstrap açılır listesiyle çalışıyorum. Ancak metin bu noktada mevcut içerik olarak daha geniş olabilir (ve bunu css aracılığıyla çözmenin pek çok iyi yolu yoktur).

Kullandım:

$table.css({ position: "absolute", visibility: "hidden", display: "table" });

bunun yerine, konteyneri, içerik daha genişse her zaman genişlikte ölçeklenen bir tabloya ayarlar.


0
jQuery(".megamenu li.level0 .dropdown-container .sub-column ul .level1").on("mouseover", function () {
    var sum = 0;
    jQuery(this).find('ul li.level2').each(function(){
    sum -= jQuery(this).height();
    jQuery(this).parent('ul').css('margin-top', sum / 1.8);
    console.log(sum);
    });
});

Üzerine gelindiğinde liste öğesinin yüksekliğini hesaplayabiliriz.


0

Daha önce söylendiği gibi, başka bir yere klonlama ve ekleme yöntemi, stil farklı olabileceği için aynı sonuçları garanti etmez.

Benim yaklaşımım aşağıdadır. Saklanmaktan sorumlu ebeveyni arayan ebeveynleri yukarı çıkarır, ardından gerekli genişliği, yüksekliği vb. Hesaplamak için geçici olarak gizler.

    var width = parseInt($image.width(), 10);
    var height = parseInt($image.height(), 10);

    if (width === 0) {

        if ($image.css("display") === "none") {

            $image.css("display", "block");
            width = parseInt($image.width(), 10);
            height = parseInt($image.height(), 10);
            $image.css("display", "none");
        }
        else {

            $image.parents().each(function () {

                var $parent = $(this);
                if ($parent.css("display") === "none") {

                    $parent.css("display", "block");
                    width = parseInt($image.width(), 10);
                    height = parseInt($image.height(), 10);
                    $parent.css("display", "none");
                }
            });
        }
    }

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.