【问题标题】:jQuery - Get Width of Element when Not Visible (Display: None)jQuery - 不可见时获取元素的宽度(显示:无)
【发布时间】:2010-12-01 03:12:33
【问题描述】:

似乎在 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,因为它不可见(这是我的猜测,因为它在可见时给了我一个数字)。有没有办法在不使父级可见的情况下获得表格的宽度?

【问题讨论】:

    标签: jquery width


    【解决方案1】:

    我试图找到隐藏元素的工作函数,但我意识到 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

    【讨论】:

    • CSS 比大家想象的要复杂得多 这是真的……
    【解决方案2】:

    如前所述,clone and attach else 方法并不能保证相同的结果,因为样式可能不同。

    以下是我的方法。它向上移动父母寻找负责隐藏的父母,然后暂时取消隐藏以计算所需的宽度、高度等。

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

    【讨论】:

      【解决方案3】:
      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);
          });
      });
      

      悬停时我们可以计算列表项的高度。

      【讨论】:

        【解决方案4】:

        除了the answer posted by Tim Banks,在解决我的问题之后,我还必须编辑一件简单的事情。

        其他人可能有同样的问题;我正在使用下拉菜单中的 Bootstrap 下拉菜单。但是此时文本可以像当前内容一样宽(并且通过 css 解决这个问题的好方法并不多)。

        我用过:

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

        相反,它将容器设置为一个表格,如果内容更宽,该表格总是按比例缩放。

        【讨论】:

          【解决方案5】:

          这是我使用的一个技巧。它涉及添加一些 CSS 属性以使 jQuery 认为该元素是可见的,但实际上它仍然是隐藏的。

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

          这是一种 hack,但对我来说似乎很好用。

          更新

          我已经写了一个 blog post 来涵盖这个主题。上面使用的方法可能会出现问题,因为您将 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));
          

          【讨论】:

          • 这不会让浏览器重绘页面两次吗?如果是这样,这是一个次优的解决方案。
          • @Tim Banks 非常好!我实际上写了一个类似的扩展。我注意到的是 Firefox 中的非常奇怪的行为,width() 对于你和我的插件都返回 0。最重要的是,它从不考虑填充。 jsfiddle.net/67cgB。我很难弄清楚还有什么办法可以解决它。
          • 如何在 jquery 手风琴中使用这个 hack 来获取隐藏的手风琴尺寸?需要您的帮助。
          • @FercoCQ 我添加了参考博客文章中的代码。
          • .andSelf() is deprecated in jQuery 1.8+, and removed in jQuery 3+。如果您使用的是 jQuery 1.8+,请将 .andSelf() 替换为 .addBack()
          【解决方案6】:

          如果您需要隐藏某些内容的宽度,并且无论出于何种原因无法取消隐藏,您可以克隆它,更改 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();
          

          【讨论】:

          • 如果元素尺寸受到父容器或更复杂的 CSS 选择器的影响,基于布局层次结构,它将不起作用。
          【解决方案7】:

          一种解决方案,虽然它不适用于所有情况,但通过将不透明度设置为 0 来隐藏元素。完全透明的元素将具有宽度。

          缺点是元素仍会占用空间,但这在所有情况下都不是问题。

          例如:

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

          【讨论】:

            【解决方案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 吗?另外,好奇您使用顶部/左侧做什么?我只使用“宽度”,但想知道我是否应该出于某种原因关注这些偏移量。
            【解决方案9】:

            这里大多数解决方案遗漏的最大问题是元素的宽度通常由 CSS 根据其在 html 中的作用域进行更改。

            如果我通过将一个元素的克隆附加到 body 来确定它的 offsetWidth,当它具有仅适用于其当前范围的样式时,我会得到错误的宽度。

            例如:

            //css

            .module-container .my-elem{ 边框:60px 纯黑色; }

            现在,当我尝试在 body 上下文中确定 my-elem 的宽度时,它将超出 120 像素。您可以改为克隆模块容器,但您的 JS 不必知道这些细节。

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

            如果可能,请使用 visibility: hidden 代替。这仍然会渲染元素,让您可以毫不费力地计算宽度。

            【讨论】:

              【解决方案10】:

              在取宽度之前使父显示显示,然后取宽度,最后使父显示隐藏。跟关注一样

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

              【讨论】:

                【解决方案11】:

                感谢您发布上面的 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 有“max-width: 100%”,“realWidth”函数克隆它并将其附加到正文。 如果图像的原始尺寸大于主体,那么您将获得主体的尺寸,而不是该图像的实际尺寸。

                【讨论】:

                  【解决方案12】:
                  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" );)
                  • 只是指出,如果你的元素从任何父元素继承了它的宽度,这将不起作用。它只会继承不正确的正文宽度。
                  • 我将clone.css("visibility","hidden");修改为clone.css({"visibility":"hidden", "display":"table"});,解决了@Dean指出的问题
                  • 太好了,谢谢!它对我的情况几乎没有什么变化:我已经对其进行了更改以支持要克隆的对象和其中的选择器以获得它的真实宽度。并非所有时候我们都想测量隐藏的同一个根对象。
                  • 我曾经使用相同的方法,但那天我得到了很多反对意见。奇怪的是你有这么多赞成票:D 我会告诉你为什么这是不好的解决方案以及几年前我的回答中指出的内容。一旦您复制元素并将其直接放入正文中,您肯定会删除与该特定元素相关的所有 CSS。例如。从这个:#some wrapper .hidden_element{/*Some CSS*/} 你正在做这个:body .hidden_element#some_wrapper .hidden_element 不再使用!因此,您计算的尺寸可能与原始尺寸不同。 TLTR:你只是失去了风格
                  猜你喜欢
                  • 2014-09-04
                  • 2017-05-04
                  • 2016-12-17
                  • 1970-01-01
                  • 2014-03-20
                  • 2013-12-08
                  • 1970-01-01
                  • 2012-12-17
                  • 1970-01-01
                  相关资源
                  最近更新 更多