【问题标题】:jQuery: quickest way to determine if at least one child in a container is visiblejQuery:确定容器中至少一个孩子是否可见的最快方法
【发布时间】:2012-07-09 21:22:23
【问题描述】:

确定div 容器中是否至少有一个子div 可见的最快方法是什么?

我一直在用这个:

if ($this.children('div:visible').length) {...

但它很慢,因为它会检查所有孩子(其中有很多)。

我猜最快的方法会在找到第一个可见元素后立即停止搜索,但最快的方法获胜:)

顺便说一句:

我的实际情况是一个包含大约 100 个 次要容器,每个容器最多包含 100 个元素。我想要 确定哪些次要容器具有至少一个可见元素。 底部的元素被各种隐藏和显示 类。

谢谢。

【问题讨论】:

    标签: javascript jquery performance visible


    【解决方案1】:

    试试这个,使用 each 进行迭代并在匹配时返回 false 可以减少迭代次数。

    isAtleastVisible = false;
    $this.children('div').each(function (){
        if($(this).is(':visible') == true)
        {
           isAtleastVisible = true;
           return false;  //This will break each loop at the first visible div
        }
    });
    
    if(isAtleastVisible)
       alert("atleast one is visible");
    else
       alert("None is visible");
    

    这将提高性能,它取决于可见 div 的第一次出现。如果是百分之一,则只需要一次迭代,如果是百分之一,则需要一百次迭代,并且不会有任何性能提升。

    正如 @Rob W 在回答有关 is('visible') 的工作时解释的那样,该函数通过这些参数检查可见性。

    1. 宽高为零
    2. style.display 没有

    假设通过设置 display = none 隐藏元素,您可以使用其中之一来提高性能。这种情况可以用 javascript 语句 .style.display == 'block' 替换 jQuery 方法 .is(':visible') == true

        isAtleastVisible = false;
        $this.children('div').each(function (){
            if(this.style.display == 'block')
            {
               isAtleastVisible = true;
               return false;  //This will break each loop at the first visible div
            }
        });
    

    【讨论】:

    • 这是此页面上唯一不正确的解决方案。 .is('visible') 检查所选项目是否为 <visible> 元素。 .is() 的正确实现发布在 this answer 下(尽管它包含过时的双 jQuery 包装:$($(...)))。
    • 感谢@Rob W,我已尝试改进我的答案,请看一下。
    • 该建议没有必要起作用:style.display 可能是 ""(并且仍然可见)或者父级可能被隐藏。该建议非常具体:例如,它不考虑display:table-cell;
    【解决方案2】:
    var isVisible;
    $(this.children('div')).each(function() {
        if ($(this).is(":visible")) {
            isVisible = true;
            return false;
        }
    });
    

    【讨论】:

      【解决方案3】:

      一种方法是使用each() 遍历子项。但是,如果可见元素是最后一个元素,这将与您的代码一样慢:)

      另一种选择是检查容器的高度。

      if($('#container').height() > 0) { ... }
      

      如果孩子占用空间(高度),那么容器的高度也将 > 0。

      【讨论】:

      • 如果孩子绝对定位怎么办?那么支票就不可靠了。
      • 没错。它不会在所有情况下都有效,但可能适用于他的情况:) 如果适用于他的情况,它会非常快
      • 我 +1 是因为它确实很快(如果事先知道并满足确切条件,则可靠)。我没有在我的测试用例中包含你的代码,因为它没有可比性。
      【解决方案4】:

      自己动手:

      var $container = $('#container'), $children = $container.children(), found = false;
      
      for (var i = 0; i < $container.length; i++) {
          var elem = $children[i];
          if (!( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none")){
              found = true;
              break;
          }
      }
      

      source

      【讨论】:

      • return 在循环内?你的意思是break
      • 您的代码不限于divs。我正在创建一个 JSPerf 来测试所有方法。我将.children() 替换为.children('div'),以使比较相等。编辑:widthheight 未定义,在测试用例中抛出错误。
      • 谢谢。我正在与 Rob 合作,但这也很勇敢:)
      【解决方案5】:

      本页所有方法的JSPerf

      已编辑:我原来的方法更快的原因是它使用了.find('&gt;' + selector),其中.children()必须使用(循环遍历所有子元素,并检查元素是否匹配选择器)。

      由于div 是本机支持的选择器,并且测试用例不包含深度嵌套的元素,因此我的解决方案速度很快。但是在对其进行规范化之后,它看起来几乎等于 qwertymks 解决方案。这两种方案的 JSPerf 会显示他的方案稍微快一点,因为它少了一个函数调用。

      本页的解决方案是通用的:下面的代码可以针对特定情况进行优化(例如选择器只是一个标签的事实):http://jsfiddle.net/kFZJs/


      为了加快进度,拆分选择器,因为:visible不是原生CSS选择器。

      首选的解决方案应该使用尽可能少的 jQuery,因为所需的解决方案必须是高性能的。为此,examine the logic of :visible

      原始函数包含jQuery.support.reliableHiddenOffsets。当您的孩子不是表格单元格(仅在 IE8- 中使用)时,可以安全地剥离这有利于性能。

      现在,写一个 jQuery 插件(不贵):

       (function($) {
           $.fn.hasAtLeastOneVisibleChild = function(selector) {
               var $col = this.children(selector), i, elem;
               for (i=0; i<$col.length; i++) {
                   var elem = $col[i];
                   if (elem.offsetWidth !== 0 || elem.offsetHeight !== 0) {
                       return true;
                   }
               }
               return false;
           };
       })(jQuery);
       // Usage:
       $this.hasAtLeastOneVisibleChild('div'); // True or false
      

      【讨论】:

      • $elem 具有误导性,应该是 elem 但仍然获得 +1
      • 谢谢。我正在尝试实施您的解决方案,但遇到了障碍。这是测试实际问题的代码。 $('#clf').children('ul').each(function (i) {var $that = $(this);$that.children().each(function (){if ($(this).hasAtLeastOneVisibleChild('li')) {console.log("Found one for " + $that.attr('id'));} else {console.log("Found none for " + $that.attr('id'));}});}); 虽然 (13, 17...),但每个 ul 都有多个 console.logs。你明白为什么吗?
      • @Nick 找到匹配项后,您必须使用return false; 跳出循环。另请参阅文档:api.jquery.com/each
      • @qwertymk 感谢现场,我已经编辑了该部分,并包含了 JSPerf 测试。
      • @Nick 值得提出一个新问题。差异是由原型链与范围链查找引起的。后者比前者快:jsperf.com/scope-chain-vs-prototype-chain-lookup。但是不要将函数声明放在循环中:创建函数不是免费的。
      猜你喜欢
      • 2023-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-26
      • 1970-01-01
      • 2014-04-26
      • 1970-01-01
      • 2014-06-19
      相关资源
      最近更新 更多