jQuery - Dapatkan Lebar Elemen saat Tidak Terlihat (Tampilan: Tidak Ada)

109

Sepertinya di jQuery ketika elemen tidak terlihat, lebar () mengembalikan 0. Masuk akal, tapi saya perlu mendapatkan lebar tabel untuk mengatur lebar induk sebelum saya menunjukkan induk.

Seperti disebutkan di bawah, ada teks di induk, yang membuat induk miring dan terlihat menjijikkan. Saya ingin induk hanya selebar tabel dan memiliki bungkus teks.

<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 selalu mengembalikan 0 karena tidak terlihat (tebakan saya karena memberi saya angka saat terlihat). Apakah ada cara untuk mendapatkan lebar tabel tanpa membuat induknya terlihat?

Martin
sumber

Jawaban:

95

Ini adalah trik yang saya gunakan. Ini melibatkan penambahan beberapa properti CSS untuk membuat jQuery berpikir bahwa elemen tersebut terlihat, tetapi sebenarnya itu masih tersembunyi.

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

Ini semacam peretasan, tetapi tampaknya berfungsi dengan baik untuk saya.

MEMPERBARUI

Saya telah menulis posting blog yang membahas topik ini. Metode yang digunakan di atas berpotensi bermasalah karena Anda menyetel ulang properti CSS menjadi nilai kosong. Bagaimana jika mereka memiliki nilai sebelumnya? Solusi yang diperbarui menggunakan metode swap () yang ditemukan di kode sumber jQuery.

Kode dari posting blog yang direferensikan:

//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));
Tim Banks
sumber
1
Bukankah ini akan membuat browser menggambar ulang halaman dua kali? Jika demikian, ini adalah solusi yang kurang optimal.
marcusklaas
@Tim Banks sangat bagus! Saya sebenarnya menulis ekstensi serupa. Apa yang saya perhatikan adalah perilaku yang sangat aneh di Firefox , width () mengembalikan 0, untuk plugin Anda dan saya keduanya. Dan di atas itu semua tidak pernah menjadi faktor dalam padding. jsfiddle.net/67cgB . Saya mengalami waktu yang paling sulit untuk mencari tahu apa lagi yang dapat saya lakukan untuk memperbaikinya.
Mark Pieszak - Trilon.io
Bagaimana saya bisa menggunakan retasan ini di dalam jquery accordion untuk menyembunyikan dimensi akordeon? Butuh bantuan Anda.
Deepak Ingole
1
@FercoCQ Saya menambahkan kode dari posting blog yang direferensikan.
Tim Banks
1
.andSelf () tidak digunakan lagi di jQuery 1.8+, dan dihapus di jQuery 3+ . Jika Anda menggunakan jQuery 1.8+, ganti .andSelf () dengan .addBack () .
Tim
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"));
Fábio Paiva
sumber
trik bagus kotor !!! Pastikan bahwa klon memiliki properti css yang benar (mis. Jika ukuran font dari elemen asli adalah 15 Anda harus menggunakan clone.css ("visibility", "hidden"). Css ("font-size", "15px" );)
alex
18
Sekadar menunjukkan, ini tidak akan berfungsi jika elemen Anda mewarisi lebarnya dari induk mana pun. Itu hanya akan mewarisi lebar tubuh yang salah.
Dean
3
Aku diubah clone.css("visibility","hidden");untuk clone.css({"visibility":"hidden", "display":"table"});yang memecahkan masalah ditunjukkan oleh @Dean
isapir
Terima kasih banyak! Ini bekerja dengan sedikit perubahan untuk kasus saya: Saya telah mengubahnya untuk mendukung objek untuk dikloning dan selektor di dalamnya untuk mendapatkan lebar sebenarnya. Tidak semua waktu kami ingin mengukur objek root yang sama yang tersembunyi.
falsarella
Saya dulu menggunakan pendekatan yang sama dan saya mendapat banyak suara negatif hari itu. Aneh karena Anda memiliki begitu banyak suara positif: D Saya akan memberi tahu Anda mengapa ini adalah solusi yang buruk dan apa yang ditunjukkan di bawah jawaban saya beberapa tahun yang lalu. Setelah Anda menyalin elemen dan memasukkannya langsung ke body, pasti Anda menghapus semua CSS yang terkait dengan elemen khusus ini. Sebagai contoh. dari ini: #some wrapper .hidden_element{/*Some CSS*/}Anda membuat ini: body .hidden_element. Tidak #some_wrapper .hidden_elementdigunakan lagi! Akibatnya, ukuran yang dihitung mungkin berbeda dari ukuran aslinya. TLTR: Anda baru saja kehilangan gaya
alih
8

Berdasarkan jawaban Roberts, inilah fungsi saya. Ini berfungsi untuk saya jika elemen atau induknya telah pudar melalui jQuery, bisa mendapatkan dimensi dalam atau luar dan juga mengembalikan nilai offset.

/ edit1: menulis ulang fungsinya. sekarang lebih kecil dan bisa dipanggil langsung ke objek

/ edit2: fungsi sekarang akan memasukkan klon tepat setelah elemen asli, bukan badan, sehingga memungkinkan klon mempertahankan dimensi yang diwariskan.

$.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();
Marco Kerwitz
sumber
Versi YUI untuk mereka yang membutuhkan: gist.github.com/samueljseay/13c2e69508563b2f0458
Novisiat Kode
Apakah offsetTop benar untuk item yang diberikan bahwa itu adalah tiruan dan bukan item 'asli'? Haruskah Anda menggunakan $ this.offset (). Top? Juga, penasaran untuk apa Anda menggunakan bagian atas / kiri? Saya hanya menggunakan 'lebar' tetapi bertanya-tanya apakah saya harus memperhatikan offset itu karena alasan tertentu.
Terry
3

Terima kasih telah memposting fungsi realWidth di atas, itu sangat membantu saya. Berdasarkan fungsi "realWidth" di atas, saya menulis, reset CSS, (alasan dijelaskan di bawah).

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" mendapatkan lebar dari tag yang ada. Saya menguji ini dengan beberapa tag gambar. Masalahnya adalah, ketika gambar diberi CSS dimensi per lebar (atau max-width), Anda tidak akan pernah mendapatkan dimensi sebenarnya dari gambar itu. Mungkin, img memiliki "max-width: 100%", fungsi "realWidth" mengkloningnya dan menambahkannya ke badan. Jika ukuran asli gambar lebih besar dari badan, maka Anda mendapatkan ukuran badan dan bukan ukuran sebenarnya dari gambar tersebut.

Robert
sumber
3

Saya mencoba menemukan fungsi kerja untuk elemen tersembunyi tetapi saya menyadari bahwa CSS jauh lebih kompleks daripada yang dipikirkan semua orang. Ada banyak teknik tata letak baru di CSS3 yang mungkin tidak berfungsi untuk semua jawaban sebelumnya seperti kotak fleksibel, kisi, kolom, atau bahkan elemen di dalam elemen induk yang kompleks.

contoh flexibox masukkan deskripsi gambar di sini

Saya pikir satu-satunya solusi berkelanjutan & sederhana adalah rendering waktu nyata . Pada saat itu, browser harus memberi Anda ukuran elemen yang benar .

Sayangnya, JavaScript tidak menyediakan acara langsung apa pun untuk memberi tahu ketika elemen ditampilkan atau disembunyikan. Namun, saya membuat beberapa fungsi berdasarkan DOM Attribute Modified API yang akan menjalankan fungsi callback ketika visibilitas elemen diubah.

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

    // render or adjust something
});

Untuk informasi lebih lanjut, Silakan kunjungi di proyek saya GitHub.

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

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

Soul_Master
sumber
4
CSS jauh lebih kompleks daripada yang dipikirkan semua orang. Ini sangat benar…
Halo
3

Jika Anda membutuhkan lebar dari sesuatu yang tersembunyi dan Anda tidak dapat menyembunyikannya karena alasan apa pun, Anda dapat mengkloningnya, mengubah CSS sehingga ditampilkan dari halaman, membuatnya tidak terlihat, lalu mengukurnya. Pengguna tidak akan lebih bijak jika disembunyikan dan dihapus setelahnya.

Beberapa jawaban lain di sini hanya membuat visibilitas tersembunyi yang berfungsi, tetapi itu akan mengambil tempat kosong di halaman Anda untuk sepersekian detik.

Contoh:

$itemClone = $('.hidden-item').clone().css({
        'visibility': 'hidden',
        'position': 'absolute',
        'z-index': '-99999',
        'left': '99999999px',
        'top': '0px'
    }).appendTo('body');
var width = $itemClone.width();
$itemClone.remove();
rannmann
sumber
2
Ini tidak akan berfungsi jika dimensi elemen dipengaruhi oleh penampung induk atau pemilih CSS yang lebih kompleks, berdasarkan hierarki tata letak.
Sebastian Nowak
2

Sebelum mengambil lebar buat tampilan induknya tampil, lalu ambil lebarnya dan terakhir buat tampilan induknya sembunyi. Sama seperti mengikuti

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

Salah satu solusinya, meskipun tidak akan berfungsi di semua situasi, adalah menyembunyikan elemen dengan menyetel opasitas ke 0. Elemen yang sepenuhnya transparan akan memiliki lebar.

Kekurangannya adalah bahwa elemen tersebut akan tetap menggunakan ruang, tetapi itu tidak akan menjadi masalah di semua kasus.

Sebagai contoh:

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

Masalah terbesar yang terlewatkan oleh sebagian besar solusi di sini adalah lebar elemen sering diubah oleh CSS berdasarkan cakupannya dalam html.

Jika saya menentukan offsetWidth elemen dengan menambahkan tiruannya ke body ketika memiliki gaya yang hanya berlaku dalam cakupan saat ini, saya akan mendapatkan lebar yang salah.

sebagai contoh:

// css

.module-container .my-elem {border: 60px solid black; }

sekarang ketika saya mencoba untuk menentukan lebar my-elem dalam konteks body itu akan menjadi 120px. Anda dapat menggandakan penampung modul sebagai gantinya, tetapi JS Anda tidak harus mengetahui detail ini.

Saya belum mengujinya tetapi pada dasarnya solusi Soul_Master tampaknya menjadi satu-satunya yang dapat berfungsi dengan baik. Namun sayangnya melihat implementasinya, kemungkinan akan mahal untuk digunakan dan buruk untuk kinerja (karena sebagian besar solusi yang disajikan di sini juga.)

Jika memungkinkan, gunakan visibility: hidden sebagai gantinya. Ini masih akan membuat elemen, memungkinkan Anda menghitung lebar tanpa repot.

Kode Novisiat
sumber
0

Selain jawaban yang diposting oleh Tim Banks , yang mengikuti untuk memecahkan masalah saya, saya harus mengedit satu hal sederhana.

Orang lain mungkin memiliki masalah yang sama; Saya bekerja dengan dropdown Bootstrap dalam menu dropdown. Tetapi teks bisa lebih luas sebagai konten saat ini pada saat ini (dan tidak banyak cara yang baik untuk menyelesaikannya melalui css).

Saya menggunakan:

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

sebagai gantinya, yang menyetel wadah ke tabel yang selalu berskala lebar jika isinya lebih lebar.

Mathijs Segers
sumber
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);
    });
});

Saat melayang kita dapat menghitung tinggi item daftar.

rajashekar
sumber
0

Seperti yang telah dikatakan sebelumnya, metode klon dan lampirkan di tempat lain tidak menjamin hasil yang sama karena gaya mungkin berbeda.

Di bawah ini adalah pendekatan saya. Itu berjalan ke atas orang tua mencari orang tua yang bertanggung jawab atas persembunyian, lalu sementara menampilkannya untuk menghitung lebar, tinggi yang diperlukan, dll.

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

Ibraheem
sumber