jQuery - รับความกว้างขององค์ประกอบเมื่อมองไม่เห็น (แสดง: ไม่มี)


109

ดูเหมือนว่าใน jQuery เมื่อองค์ประกอบไม่สามารถมองเห็นได้ width () ส่งกลับ 0 เข้าท่า แต่ฉันต้องได้รับความกว้างของตารางเพื่อตั้งค่าความกว้างของพาเรนต์ก่อนที่ฉันจะแสดงพาเรนต์

ดังที่ระบุไว้ด้านล่างมีข้อความในผู้ปกครองซึ่งทำให้ผู้ปกครองเอียงและดูน่ารังเกียจ ฉันต้องการให้พาเรนต์กว้างเท่ากับตารางเท่านั้นและมีการตัดข้อความ

<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 จะคืนค่า 0 เสมอเนื่องจากมองไม่เห็น (คือการคาดเดาของฉันเนื่องจากให้ตัวเลขแก่ฉันเมื่อมองเห็นได้) มีวิธีรับความกว้างของตารางโดยไม่ทำให้ผู้ปกครองมองเห็นได้หรือไม่?

คำตอบ:


95

นี่คือเคล็ดลับที่ฉันใช้ มันเกี่ยวข้องกับการเพิ่มคุณสมบัติ CSS บางอย่างเพื่อให้ jQuery คิดว่าองค์ประกอบนั้นมองเห็นได้ แต่ในความเป็นจริงมันยังถูกซ่อนอยู่

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

มันเป็นการแฮ็ก แต่ดูเหมือนว่าจะใช้ได้ดีสำหรับฉัน

อัปเดต

ฉันได้เขียนโพสต์บล็อกที่ครอบคลุมหัวข้อนี้ วิธีที่ใช้ข้างต้นอาจเป็นปัญหาได้เนื่องจากคุณรีเซ็ตคุณสมบัติ CSS เป็นค่าว่าง เกิดอะไรขึ้นถ้าพวกเขามีค่าก่อนหน้านี้? โซลูชันที่อัปเดตใช้วิธีการ swap () ที่พบในซอร์สโค้ด jQuery

รหัสจากบล็อกโพสต์อ้างอิง:

//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
สิ่งนี้จะไม่ทำให้เบราว์เซอร์วาดหน้าซ้ำสองครั้งหรือไม่? ถ้าเป็นเช่นนั้นนี่เป็นวิธีแก้ปัญหาที่ไม่เหมาะสม
marcusklaas

@ Tim Banks ดีมาก! ฉันเขียนส่วนขยายที่คล้ายกันจริงๆ สิ่งที่ฉันสังเกตเห็นคือพฤติกรรมแปลก ๆ ในFirefoxโดย width () จะคืนค่า 0 สำหรับปลั๊กอินของคุณและของฉันทั้งคู่ และยิ่งไปกว่านั้นมันไม่เคยเป็นปัจจัยในการเพิ่มช่องว่าง jsfiddle.net/67cgB . ฉันมีช่วงเวลาที่ยากลำบากที่สุดในการหาสิ่งที่ต้องทำเพื่อแก้ไข
Mark Pieszak - Trilon.io

ฉันจะใช้แฮ็คนี้ในหีบเพลง jquery เพื่อหาขนาดหีบเพลงที่ซ่อนอยู่ได้อย่างไรต้องการความช่วยเหลือจากคุณ
Deepak Ingole

1
@FercoCQ ฉันเพิ่มรหัสจากโพสต์บล็อกอ้างอิง
Tim Banks

1
.andSelf () จะเลิกใช้ใน jQuery 1.8+ และถอดออกใน jQuery 3+ หากคุณกำลังใช้ jQuery 1.8+ แทนที่ .andSelf () กับ.addBack ()
ทิม

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

เคล็ดลับดีสกปรก !!! ตรวจสอบให้แน่ใจว่าโคลนมีคุณสมบัติ css ที่ถูกต้อง (เช่นหากขนาดตัวอักษรขององค์ประกอบดั้งเดิมคือ 15 คุณควรใช้ clone.css ("visibility", "hidden") css ("font-size", "15px" );)
alex

18
เพื่อชี้ให้เห็นว่าสิ่งนี้จะใช้ไม่ได้หากองค์ประกอบของคุณได้รับช่วงความกว้างจากระดับบน มันจะรับช่วงความกว้างของร่างกายซึ่งไม่ถูกต้อง
คณบดี

3
ฉันแก้ไขclone.css("visibility","hidden");เพื่อclone.css({"visibility":"hidden", "display":"table"});แก้ไขปัญหาที่ชี้โดย @Dean
isapir

เยี่ยมมากขอบคุณ! มันทำงานได้โดยมีการเปลี่ยนแปลงเล็กน้อยสำหรับกรณีของฉัน: ฉันได้เปลี่ยนมันเพื่อรองรับวัตถุที่จะโคลนและตัวเลือกที่อยู่ภายในเพื่อให้ได้ความกว้างที่แท้จริงของมัน ไม่ใช่ทุกครั้งที่เราต้องการวัดวัตถุรากเดียวกันที่ซ่อนอยู่
falsarella

ฉันเคยใช้วิธีการเดียวกันนี้และฉันก็ได้รับการโหวตลงคะแนนมากมายในวันนั้น แปลกที่คุณมีการโหวตเพิ่มขึ้นมากมาย: D ฉันจะบอกคุณว่าทำไมนี่ถึงเป็นทางออกที่ไม่ดีและสิ่งที่ชี้ให้เห็นภายใต้คำตอบของฉันเมื่อไม่กี่ปีที่ผ่านมา เมื่อคุณคัดลอกองค์ประกอบและใส่ลงในร่างกายโดยตรงแน่นอนที่สุดคุณกำลังลบ CSS ทั้งหมดที่เกี่ยวข้องกับองค์ประกอบเฉพาะนี้ ตัวอย่างเช่น. จากสิ่งนี้: #some wrapper .hidden_element{/*Some CSS*/}คุณกำลังทำสิ่งนี้: body .hidden_element. #some_wrapper .hidden_elementไม่ได้ใช้ใด ๆ เพิ่มเติม! ด้วยเหตุนี้ขนาดที่คำนวณของคุณอาจแตกต่างจากขนาดดั้งเดิม TLTR: คุณกำลังสูญเสียสไตล์
แทนที่จะเป็น

8

จากคำตอบของ Roberts นี่คือหน้าที่ของฉัน สิ่งนี้ใช้ได้ผลสำหรับฉันหากองค์ประกอบหรือพาเรนต์จางหายไปผ่าน jQuery สามารถรับมิติภายในหรือภายนอกและส่งคืนค่าออฟเซ็ต

/ edit1: เขียนฟังก์ชันใหม่ ตอนนี้มันเล็กลงและสามารถเรียกได้โดยตรงบนวัตถุ

/ edit2: ฟังก์ชั่นนี้จะแทรกโคลนต่อจากองค์ประกอบดั้งเดิมแทนที่จะเป็นร่างกายทำให้โคลนสามารถรักษามิติที่สืบทอดมาได้

$.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();

รุ่น YUI สำหรับผู้ที่ต้องการ: gist.github.com/samueljseay/13c2e69508563b2f0458
รหัสสามเณร

offsetTop ถูกต้องสำหรับไอเท็มหรือไม่เนื่องจากเป็นโคลนไม่ใช่ของจริง คุณควรใช้ $ this.offset (). top หรือไม่? นอกจากนี้อยากรู้ว่าคุณใช้ด้านบน / ซ้ายเพื่ออะไร? ฉันใช้แค่ 'ความกว้าง' แต่สงสัยว่าฉันควรจะกังวลเกี่ยวกับการชดเชยเหล่านั้นด้วยเหตุผลบางประการหรือไม่
Terry

3

ขอบคุณสำหรับการโพสต์ฟังก์ชั่น realWidth ด้านบนซึ่งช่วยฉันได้มาก จากฟังก์ชั่น "realWidth" ด้านบนที่ฉันเขียนคือการรีเซ็ต CSS (เหตุผลที่อธิบายไว้ด้านล่าง)

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" รับความกว้างของแท็กที่มีอยู่ ฉันทดสอบสิ่งนี้กับแท็กรูปภาพ ปัญหาคือเมื่อรูปภาพกำหนดขนาด CSS ต่อความกว้าง (หรือความกว้างสูงสุด) คุณจะไม่ได้รับขนาดจริงของรูปภาพนั้น บางที img อาจมี "ความกว้างสูงสุด: 100%" ฟังก์ชัน "realWidth" จะโคลนและต่อท้ายเข้ากับร่างกาย หากขนาดต้นฉบับของรูปภาพใหญ่กว่าร่างกายคุณจะได้ขนาดของร่างกายไม่ใช่ขนาดจริงของรูปภาพนั้น


3

ฉันพยายามค้นหาฟังก์ชันการทำงานสำหรับองค์ประกอบที่ซ่อนอยู่ แต่ฉันรู้ว่า CSS นั้นซับซ้อนกว่าที่ทุกคนคิด มีเทคนิคการจัดวางใหม่ ๆ มากมายใน CSS3 ที่อาจใช้ไม่ได้กับคำตอบก่อนหน้านี้ทั้งหมดเช่นกล่องแบบยืดหยุ่นตารางคอลัมน์หรือแม้แต่องค์ประกอบภายในองค์ประกอบหลักที่ซับซ้อน

ตัวอย่าง flexibox ใส่คำอธิบายภาพที่นี่

ผมคิดว่าเพียงการแก้ปัญหาอย่างยั่งยืนและง่ายๆคือการแสดงผลแบบ real-time ในขณะที่เบราว์เซอร์ควรให้คุณทราบว่าขนาดขององค์ประกอบที่ถูกต้อง

น่าเศร้าที่ JavaScript ไม่มีเหตุการณ์โดยตรงที่จะแจ้งเตือนเมื่อมีการแสดงหรือซ่อนองค์ประกอบ อย่างไรก็ตามฉันสร้างฟังก์ชันบางอย่างตาม DOM Attribute Modified API ซึ่งจะเรียกใช้ฟังก์ชันเรียกกลับเมื่อการมองเห็นองค์ประกอบเปลี่ยนไป

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

    // render or adjust something
});

สำหรับข้อมูลเพิ่มเติมโปรดไปที่ GitHub โครงการของฉัน

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

การสาธิต: http://jsbin.com/ETiGIre/7


4
CSS ซับซ้อนกว่าที่ทุกคนคิด นี่เป็นเรื่องจริง…
dgellow

3

หากคุณต้องการความกว้างของสิ่งที่ซ่อนอยู่และคุณไม่สามารถยกเลิกการซ่อนได้ไม่ว่าจะด้วยเหตุผลใดก็ตามคุณสามารถโคลนเปลี่ยน CSS เพื่อให้แสดงออกจากหน้าทำให้มองไม่เห็นจากนั้นจึงวัดค่า ผู้ใช้จะไม่ฉลาดไปกว่านี้หากถูกซ่อนและถูกลบในภายหลัง

คำตอบอื่น ๆ บางส่วนที่นี่ทำให้การมองเห็นถูกซ่อนซึ่งใช้ได้ผล แต่จะใช้เวลาว่างบนหน้าของคุณเพียงเสี้ยววินาที

ตัวอย่าง:

$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
จะใช้ไม่ได้หากมิติขององค์ประกอบได้รับอิทธิพลจากคอนเทนเนอร์หลักหรือตัวเลือก CSS ที่ซับซ้อนมากขึ้นตามลำดับชั้นของโครงร่าง
Sebastian Nowak

2

ก่อนที่จะใช้ความกว้างให้แสดงพาเรนต์แสดงจากนั้นใช้ความกว้างและสุดท้ายทำให้การแสดงผลหลักซ่อน เช่นเดียวกับการติดตาม

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

2

วิธีแก้ปัญหาวิธีหนึ่งแม้ว่าจะใช้ไม่ได้ในทุกสถานการณ์ แต่ก็คือการซ่อนองค์ประกอบโดยตั้งค่าความทึบเป็น 0 องค์ประกอบที่โปร่งใสทั้งหมดจะมีความกว้าง

ข้อเสียเปรียบคือองค์ประกอบจะยังคงใช้พื้นที่ แต่นั่นจะไม่ใช่ปัญหาในทุกกรณี

ตัวอย่างเช่น:

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

1

ปัญหาใหญ่ที่สุดที่ทางแก้ส่วนใหญ่พลาดคือ CSS มักจะเปลี่ยนความกว้างขององค์ประกอบตามตำแหน่งที่กำหนดเป็น html

ถ้าฉันจะกำหนด offsetWidth ขององค์ประกอบโดยการต่อท้ายโคลนของมันเข้ากับร่างกายเมื่อมันมีสไตล์ที่ใช้เฉพาะในขอบเขตปัจจุบันฉันจะได้รับความกว้างที่ไม่ถูกต้อง

ตัวอย่างเช่น:

// css

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

ตอนนี้เมื่อฉันพยายามกำหนดความกว้างขององค์ประกอบของฉันในบริบทของร่างกายมันจะออกมา 120px คุณสามารถโคลนคอนเทนเนอร์โมดูลแทนได้ แต่ JS ของคุณไม่จำเป็นต้องรู้รายละเอียดเหล่านี้

ฉันยังไม่ได้ทดสอบ แต่โดยพื้นฐานแล้วโซลูชันของ Soul_Master ดูเหมือนจะเป็นโซลูชันเดียวที่สามารถทำงานได้อย่างถูกต้อง แต่น่าเสียดายที่เมื่อมองไปที่การนำไปใช้งานอาจมีค่าใช้จ่ายสูงและไม่ดีต่อประสิทธิภาพ (เนื่องจากโซลูชันส่วนใหญ่ที่นำเสนอในที่นี้ก็มีเช่นกัน)

ถ้าเป็นไปได้ให้ใช้ visibility: hidden แทน สิ่งนี้จะยังคงแสดงผลองค์ประกอบทำให้คุณสามารถคำนวณความกว้างได้โดยไม่ต้องยุ่งยาก


0

นอกเหนือจากคำตอบที่โพสต์โดย Tim Banksซึ่งตามมาเพื่อแก้ปัญหาของฉันฉันต้องแก้ไขสิ่งง่ายๆอย่างหนึ่ง

คนอื่นอาจมีปัญหาเดียวกัน ฉันกำลังทำงานกับรายการแบบเลื่อนลง Bootstrap ในเมนูแบบเลื่อนลง แต่ข้อความสามารถกว้างขึ้นได้เนื่องจากเนื้อหาปัจจุบัน ณ จุดนี้ (และไม่มีวิธีที่ดีมากมายในการแก้ไขผ่าน css)

ฉันใช้:

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

แทนซึ่งจะตั้งค่าคอนเทนเนอร์เป็นตารางที่มีความกว้างเสมอหากเนื้อหากว้างขึ้น


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

เมื่อวางเมาส์เหนือเราสามารถคำนวณความสูงของรายการได้


0

ดังที่ได้กล่าวไปแล้วก่อนหน้านี้การโคลนและแนบวิธีอื่นไม่ได้รับประกันว่าจะได้ผลลัพธ์เช่นเดียวกันเนื่องจากการจัดแต่งทรงผมอาจแตกต่างกัน

ด้านล่างนี้คือแนวทางของฉัน มันเดินทางไปตามผู้ปกครองเพื่อค้นหาผู้ปกครองที่รับผิดชอบการซ่อนตัวจากนั้นยกเลิกการซ่อนชั่วคราวเพื่อคำนวณความกว้างความสูงและอื่น ๆ ที่ต้องการ

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.