jQuery-在不可见时获取元素的宽度(显示:无)


108

看起来在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,因为它不可见(我想是因为它在可见时给了我一个数字)。有没有一种方法可以在不使父对象可见的情况下获得表格的宽度?

Answers:


94

这是我使用的一个技巧。它涉及添加一些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属性重置为空值,因此上面使用的方法可能会出现问题。如果他们以前有价值观怎么办?更新的解决方案使用在jQuery源代码中找到的swap()方法。

来自引用的博客文章的代码:

//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我从引用的博客文章中添加了代码。
蒂姆·班克斯

1
.andSelf()在jQuery 1.8+中已弃用,在jQuery 3+中已删除。如果您使用的是jQuery 1.8+,请将.andSelf()替换为.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"));

肮脏的好把戏!确保克隆具有正确的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 2015年

十分感谢!对于我的情况,它几乎没有变化:我对其进行了更改,以支持克隆对象和其中的选择器以获取其实际宽度。并非所有时候我们都希望测量隐藏的相同根对象。
falsarella

我曾经使用相同的方法,那天我得到了很多反对意见。很奇怪您有这么多赞成票:D我会告诉您为什么这是不好的解决方案,以及几年前我的回答指出了什么。一旦复制了元素并将其直接放入正文中,最肯定的是您将删除与此特定元素相关的所有CSS。例如。来自:#some wrapper .hidden_element{/*Some CSS*/}您正在制作:body .hidden_element。在#some_wrapper .hidden_element不使用任何更多!如此一来,您计算出的大小可能会与原始大小有所不同。TLTR:您只是在失去样式
取而代之的是

8

根据罗伯茨的回答,这是我的功能。如果该元素或其父元素已通过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吗?另外,好奇您使用顶部/左侧的目的是什么?我只使用“宽度”,但想知道是否由于某些原因我应该担心这些偏移量。
特里

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示例 在此处输入图片说明

我认为唯一可持续和简单的解决方案是实时渲染。当时,浏览器应该为您提供正确的元素大小

可悲的是,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 2014年

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选择器(基于布局层次结构)影响,则将不起作用。
塞巴斯蒂安·诺瓦克

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 {边框:60px纯黑色;}

现在,当我尝试在主体的上下文中确定元素的宽度时,它将超出120px。您可以克隆模块容器,但是您的JS不必知道这些详细信息。

我尚未测试过,但本质上Soul_Master的解决方案似乎是唯一可以正常工作的解决方案。但是不幸的是,着眼于实现,它可能使用起来成本高昂,并且对性能不利(因为这里介绍的大多数解决方案也是如此)。

如果有可能,请改用“能见度:隐藏”。这仍然会渲染元素,允许您计算宽度而无需大惊小怪。


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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.