jQuery - Nhận chiều rộng của phần tử khi không hiển thị (Hiển thị: Không có)


108

Có vẻ như trong jQuery khi một phần tử không hiển thị width () sẽ trả về 0. Có lý, nhưng tôi cần lấy chiều rộng của bảng để đặt chiều rộng của phần tử trước khi tôi hiển thị phần tử.

Như đã lưu ý bên dưới, có văn bản trong phụ huynh, khiến phụ huynh bị lệch và trông khó chịu. Tôi muốn giá trị gốc chỉ rộng bằng bảng và có dòng chữ bao quanh.

<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 luôn trả về 0 vì nó không hiển thị (tôi đoán vì nó cho tôi một số khi hiển thị). Có cách nào để lấy chiều rộng của bảng mà không làm hiển thị phần cha không?

Câu trả lời:


94

Đây là một thủ thuật tôi đã sử dụng. Nó liên quan đến việc thêm một số thuộc tính CSS để làm cho jQuery nghĩ rằng phần tử này có thể nhìn thấy, nhưng thực tế nó vẫn bị ẩn.

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

Nó là một loại hack, nhưng nó có vẻ hoạt động tốt đối với tôi.

CẬP NHẬT

Tôi đã viết một bài đăng trên blog về chủ đề này. Phương pháp được sử dụng ở trên có khả năng có vấn đề vì bạn đang đặt lại các thuộc tính CSS thành các giá trị trống. Điều gì sẽ xảy ra nếu chúng có các giá trị trước đó? Giải pháp đã cập nhật sử dụng phương thức swap () được tìm thấy trong mã nguồn jQuery.

Mã từ bài đăng blog được tham chiếu:

//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
Điều này sẽ không khiến trình duyệt vẽ lại trang hai lần? Nếu vậy, đây là một giải pháp không tối ưu.
marcusklaas

@Tim Banks rất hay! Tôi thực sự đã viết một phần mở rộng tương tự. Điều tôi nhận thấy là hành vi rất lạ trong Firefox , width () trả về 0, cho cả plugin của bạn và của tôi. Và trên hết, nó không bao giờ có yếu tố đệm. jsfiddle.net/67cgB . Tôi đang gặp khó khăn nhất trong việc tìm ra những gì khác có thể làm để khắc phục nó.
Mark Pieszak - Trilon.io

Làm cách nào tôi có thể sử dụng bản hack này bên trong đàn accordion jquery để lấy các kích thước của đàn accordion ẩn? Cần bạn giúp đỡ.
Deepak Ingole,

1
@FercoCQ Tôi đã thêm mã từ bài đăng trên blog được tham chiếu.
Tim Banks

1
.andSelf () không được dùng nữa trong jQuery 1.8+ và bị loại bỏ trong jQuery 3+ . Nếu bạn đang sử dụng jQuery 1.8+, hãy thay thế .andSelf () bằng .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"));

lừa đẹp bẩn !!! Đảm bảo rằng bản sao có các thuộc tính css phù hợp (ví dụ: nếu kích thước phông chữ của phần tử gốc là 15, bạn nên sử dụng clone.css ("khả năng hiển thị", "ẩn"). Css ("font-size", "15px" );)
alex

18
Chỉ cần lưu ý, điều này sẽ không hoạt động nếu phần tử của bạn đã kế thừa chiều rộng của nó từ bất kỳ phần tử nào. Nó sẽ chỉ kế thừa chiều rộng cơ thể không chính xác.
Dean

3
Tôi sửa đổi clone.css("visibility","hidden");để clone.css({"visibility":"hidden", "display":"table"});mà giải quyết vấn đề này chỉ ra bởi @Dean
isapir

Cảm ơn rất nhiều! Nó hoạt động với một số thay đổi nhỏ đối với trường hợp của tôi: Tôi đã thay đổi nó để hỗ trợ một đối tượng cần sao chép và một bộ chọn bên trong nó để có được chiều rộng thực của nó. Không phải lúc nào chúng ta cũng muốn đo cùng một đối tượng gốc bị ẩn.
falsarella

Tôi đã từng sử dụng cách tiếp cận tương tự và tôi đã nhận được rất nhiều phiếu phản đối vào ngày hôm đó. Kỳ lạ là Bạn có rất nhiều phiếu tán thành: D Tôi sẽ cho Bạn biết tại sao đây là giải pháp tồi và những gì đã được chỉ ra dưới câu trả lời của tôi vài năm trước. Khi Bạn sao chép phần tử và đưa trực tiếp vào phần nội dung, chắc chắn là Bạn đang xóa tất cả CSS liên quan đến phần tử cụ thể này. Ví dụ. từ này: #some wrapper .hidden_element{/*Some CSS*/}Bạn đang làm cho này: body .hidden_element. Các #some_wrapper .hidden_elementkhông được sử dụng nữa! Do đó, kích thước tính toán của bạn có thể khác với kích thước ban đầu. TLTR: Bạn chỉ đang đánh mất phong cách
thay vào đó,

8

Dựa trên câu trả lời của Roberts, đây là chức năng của tôi. Điều này phù hợp với tôi nếu phần tử hoặc phần tử gốc của nó đã bị xóa mờ qua jQuery, có thể nhận kích thước bên trong hoặc bên ngoài và cũng trả về giá trị bù đắp.

/ edit1: viết lại hàm. bây giờ nó nhỏ hơn và có thể được gọi trực tiếp trên đối tượng

/ edit2: bây giờ hàm sẽ chèn bản sao ngay sau phần tử gốc thay vì phần thân, giúp bản sao có thể duy trì các kích thước kế thừa.

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

Phiên bản YUI cho những ai cần nó: gist.github.com/samueljseay/13c2e69508563b2f0458
Code Novitiate

Liệu offsetTop có đúng với mặt hàng được cho rằng đó là hàng nhái và không phải hàng 'thật' không? Bạn có nên sử dụng $ this.offset (). Top không? Ngoài ra, hãy tò mò bạn đang sử dụng phía trên / bên trái để làm gì? Tôi chỉ sử dụng 'chiều rộng' nhưng tự hỏi liệu tôi có nên lo lắng về những hiệu số đó vì lý do gì không.
Terry

3

Cảm ơn bạn đã đăng hàm realWidth ở trên, nó thực sự giúp ích cho tôi. Dựa trên hàm "realWidth" ở trên, tôi đã viết, đặt lại CSS, (lý do được mô tả bên dưới).

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" lấy chiều rộng của thẻ hiện có. Tôi đã thử nghiệm điều này với một số thẻ hình ảnh. Vấn đề là, khi hình ảnh có kích thước CSS trên mỗi chiều rộng (hoặc chiều rộng tối đa), bạn sẽ không bao giờ nhận được kích thước thực của hình ảnh đó. Có lẽ, img có "max-width: 100%", hàm "realWidth" sao chép nó và nối nó vào phần thân. Nếu kích thước gốc của hình ảnh lớn hơn nội dung, thì bạn sẽ nhận được kích thước của phần thân chứ không phải kích thước thực của hình ảnh đó.


3

Tôi cố gắng tìm chức năng làm việc cho phần tử ẩn nhưng tôi nhận ra rằng CSS phức tạp hơn nhiều so với mọi người nghĩ. Có rất nhiều kỹ thuật bố cục mới trong CSS3 có thể không hoạt động đối với tất cả các câu trả lời trước đó như hộp linh hoạt, lưới, cột hoặc thậm chí phần tử bên trong phần tử mẹ phức tạp.

ví dụ về flexibox nhập mô tả hình ảnh ở đây

Tôi nghĩ giải pháp đơn giản và bền vững duy nhất là kết xuất thời gian thực . Tại thời điểm đó, trình duyệt sẽ cung cấp cho bạn kích thước phần tử chính xác .

Đáng buồn thay, JavaScript không cung cấp bất kỳ sự kiện trực tiếp nào để thông báo khi phần tử được hiển thị hoặc ẩn. Tuy nhiên, tôi tạo một số hàm dựa trên API sửa đổi thuộc tính DOM sẽ thực thi hàm gọi lại khi khả năng hiển thị của phần tử bị thay đổi.

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

    // render or adjust something
});

Để biết thêm thông tin, vui lòng truy cập GitHub dự án của tôi.

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

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


4
CSS phức tạp hơn nhiều so với mọi người nghĩ Điều này rất đúng…
dgellow

3

Nếu bạn cần chiều rộng của một thứ gì đó bị ẩn và bạn không thể bỏ ẩn nó vì bất kỳ lý do gì, bạn có thể sao chép nó, thay đổi CSS để nó hiển thị khỏi trang, ẩn nó và sau đó đo lường nó. Người dùng sẽ không ai khôn ngoan hơn nếu nó bị ẩn và bị xóa sau đó.

Một số câu trả lời khác ở đây chỉ làm cho khả năng hiển thị bị ẩn hoạt động, nhưng nó sẽ chiếm một vị trí trống trên trang của bạn trong một phần giây.

Thí dụ:

$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
Nó sẽ không hoạt động nếu các kích thước phần tử bị ảnh hưởng bởi vùng chứa mẹ hoặc bộ chọn CSS phức tạp hơn, dựa trên phân cấp bố cục.
Sebastian Nowak

2

Trước khi lấy chiều rộng, hãy làm cho màn hình chính hiển thị, sau đó lấy chiều rộng và cuối cùng làm cho màn hình mẹ ẩn. Cũng giống như sau

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

2

Một giải pháp, mặc dù nó sẽ không hoạt động trong mọi trường hợp, là ẩn phần tử bằng cách đặt độ mờ là 0. Một phần tử hoàn toàn trong suốt sẽ có chiều rộng.

Điểm rút lại là phần tử vẫn sẽ chiếm dung lượng, nhưng đó sẽ không phải là vấn đề trong mọi trường hợp.

Ví dụ:

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

1

Vấn đề lớn nhất bị bỏ qua bởi hầu hết các giải pháp ở đây là chiều rộng của một phần tử thường bị thay đổi bởi CSS dựa trên vị trí của nó trong html.

Nếu tôi xác định offsetWidth của một phần tử bằng cách thêm một bản sao của nó vào nội dung khi nó có các kiểu chỉ áp dụng trong phạm vi hiện tại của nó, tôi sẽ nhận được chiều rộng sai.

ví dụ:

// css

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

bây giờ khi tôi cố gắng xác định chiều rộng của my-elem trong ngữ cảnh của nội dung, nó sẽ vượt ra ngoài 120px. Thay vào đó, bạn có thể sao chép vùng chứa mô-đun, nhưng JS của bạn không cần phải biết những chi tiết này.

Tôi chưa thử nghiệm nó nhưng về cơ bản giải pháp của Soul_Master dường như là giải pháp duy nhất có thể hoạt động bình thường. Nhưng thật không may khi nhìn vào việc triển khai, nó có thể sẽ tốn kém khi sử dụng và không tốt cho hiệu suất (vì hầu hết các giải pháp được trình bày ở đây cũng vậy).

Nếu có thể, hãy sử dụng khả năng hiển thị: ẩn thay thế. Điều này sẽ vẫn hiển thị phần tử, cho phép bạn tính toán chiều rộng mà không cần phải phiền phức.


0

Ngoài câu trả lời do Tim Banks đăng tải , sau đó để giải quyết vấn đề của tôi, tôi phải chỉnh sửa một điều đơn giản.

Ai đó khác có thể có cùng một vấn đề; Tôi đang làm việc với trình đơn Bootstrap thả xuống trong trình đơn thả xuống. Nhưng văn bản có thể rộng hơn như nội dung hiện tại vào thời điểm này (và không có nhiều cách tốt để giải quyết điều đó thông qua css).

Tôi đã sử dụng:

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

thay vào đó, điều này sẽ đặt vùng chứa thành một bảng luôn mở rộng theo chiều rộng nếu nội dung rộng hơn.


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

Khi di chuột, chúng tôi có thể tính toán chiều cao mục danh sách.


0

Như đã nói trước đây, phương pháp sao chép và đính kèm ở nơi khác không đảm bảo kết quả giống như kiểu dáng có thể khác.

Dưới đây là cách tiếp cận của tôi. Nó di chuyển lên các bậc cha mẹ để tìm kiếm bậc cha mẹ chịu trách nhiệm về việc ẩn, sau đó tạm thời ẩn nó để tính toán chiều rộng, chiều cao cần thiết, v.v.

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.