【问题标题】:jQuery function similar to closest that will return elements outside of the parent chain类似于最接近的jQuery函数,它将返回父链之外的元素
【发布时间】:2012-11-30 09:03:42
【问题描述】:

是否有任何类似于closest() 的jQuery 函数将返回父链之外的元素,横向遍历?例如,我想在 div 源上调用一个函数 foo() 来返回 div 目标。我知道我可以使用 parent() 和兄弟姐妹() 进行导航,但我需要一些通用的东西,可以根据需要去往上、横向和向下等多个级别?

var allsources = $('.source');

allsources.click(function()){
  $(this).closest('.target').hide();
});

<div class="row">
  <div>
     <div class="target" ></div>
  </div>
  <div>
    <div>
      <div class="source"></div>
    </div>
  </div>
</div>

<div class="row">
  <div>
     <div class="target" ></div>
  </div>
  <div>
    <div>
      <div class="source"></div>
    </div>
  </div>
</div>

编辑:

我对最接近的定义:你有一个元素来源。试着找到它。如果找到多于一个,则返回一个更少的节点箍向下/下一个/上一个。如果没有找到,请上一级,然后尝试再次找到。重复直到没有父级。

【问题讨论】:

  • 向上、向侧面向下?那是,嗯……整个 dom 树
  • 没错,你有一个 ID,id 是唯一的(对吗?)所以你只需使用 id 选择器
  • nextAll() 或 prevAll() 在这里有用吗?
  • @tie,你所说的 closest 是什么意思不清楚。有两个轴和四个方向需要考虑(祖先链和兄弟链)。例如,如果有两个目标元素,一个在祖先链中排名较高,另外两个在兄弟链中排名较高,该怎么办?哪个最近?
  • 看看你的编辑,你到底为什么要避开明显的.parents('.row').find('.target')

标签: javascript jquery


【解决方案1】:

如果最接近的意思是“尽可能少地向上移动,然后向下移动”,那么你可以这样做

$("#source")
  .closest(":has(.target)")
  .find(".target:first")  //make sure we only select one element in case of a tie

在你的情况下,最好直接指定共同的父母:

$(this)
  .closest(".row")
  .find(".target")        //there's no tie here, no need to arbitrate

【讨论】:

  • 好主意,但这可能会返回几个元素,而不是 “最接近”的元素。
  • @FrédéricHamidi 然后需要指定 which “最接近”的元素来选择。我会随意选择。已编辑
  • 有用,但这也会获取当前父级中的元素
【解决方案2】:

这是一个棘手的问题。如前所述,您如何在这种情况下定义最接近?假设你可以决定一些规则;例如:

向上遍历:3pt
向下遍历:2 分
横向移动:1分

然后认为具有最低点的项目是“最接近的”,那么编写一个插件就很容易了,命名为closestAll,它将递归遍历整个 dom 树以确定最接近的项目。

但是,查看您最近的编辑,其中一个(众多!)正确解决所述问题的方法是:

var allsources = $('.source');

allsources.click(function(){
  $(this).parents('.row').find('.target').hide();
});

实时示例:http://jsfiddle.net/zCvJM/(源 A 只隐藏目标 A,B 相同)

【讨论】:

  • 它适用于标记上的 .row,但我只是想要一些不依赖 .row 的通用内容
  • @tie - 如果您不知道 HTML 的结构,那么您的设计就有问题。没有“1 尺寸适合所有选择器”,它几乎无法定义(参见我上面的简单尝试)。
【解决方案3】:

如果你确切知道dom的结构和嵌套层次,你有没有考虑使用eq()方法

$(this).parents().eq(1).prev().children(".target")

【讨论】:

    【解决方案4】:

    我认为除了查询整个 DOM 之外没有其他方法:

    $('#target')
    

    因为如果你想向上和穿过(也不要介意向下),那么目标元素与子元素无关。如果您还想检查子元素是否存在,则必须单独进行。

    -编辑:

    在阅读了您关于希望找到最近的元素而不管它是否是父元素的评论后,我认为您将不得不编写一个自定义函数来一次爬回 dom 一个节点。我已经测试了以下内容并且可以正常工作:

    标记

    <div id="parent">
        <div id="child1">   
            <div id="source"></div>
        </div>
        <div id="child2">
            <div class="target" rel="right"></div>
        </div>
        <div id="child3">
            <div>
                <div class="target" rel="wrong"></div>
            </div>
        </div>
    </div>
    

    脚本

    $(document).ready(function () {
        var tgt = findClosest($('#source'), '.target');
        if (tgt != undefined) {
            alert(tgt.attr('rel'));
        }
    });
    
    
    function findClosest(source, targetSel) {
        var crawledNodes = $();
        var target = null;
    
        // Go up
        source.parents().each(function () {
            console.log(crawledNodes.index($(this)));
            if (crawledNodes.index($(this)) == -1 && target == null) {
                crawledNodes.add($(this));
                target = findTarget($(this), targetSel);
    
                // Go across
                $(this).siblings().each(function () {
                    console.log("Sibling");
                    if (crawledNodes.index($(this)) == -1 && target == null) {
                        crawledNodes.add($(this));
                        target = findTarget($(this), targetSel);
                    }
                });
            }
        });
    
        return target;
    }
    
    function findTarget(el, targetSel) {
        console.log(targetSel);
        var target = el.find(targetSel);
        if (target.size() > 0) {
            return target.eq(0);
        }
        else 
        {
            return null;
        }
    }
    

    【讨论】:

    • 这不是一个糟糕的开始,但太容易被打破。例如,如果您将#child3 移动到#child1 之前,那么您的方法会找到错误的元素(example)。此元素比#child2 中的右侧元素“更远”(1up、1sideways、2down)(1up 1sideways 1down)。
    • @Jamiec 非常正确,我的脚本没有考虑兄弟姐妹的距离,因此远亲兄弟姐妹可能优先于近亲兄弟。我想这可以通过获取所有兄弟姐妹,检查它们相对于元素的索引并按最低差异对它们进行排序来完成。我的脚本还假设您不是在寻找源的兄弟姐妹或孩子。但正如你所说,这是一个开始。
    【解决方案5】:

    如果我正确理解规范,您的意思类似于下面定义的最接近的函数:

    var allsources = $(".source");
    
    function closest($source,selector) {
        if($source == null) return  $([]);
        var $matchingChildren = $source.find(selector);
        if($matchingChildren.length != 0) return $($matchingChildren.get(0));
        else return closest($source.parent(), selector)
    }
    
    allsources.click(closest($(this),'.target').hide();});
    

    你可以在http://jsfiddle.net/y2wJV/1/看到它工作

    您的定义要求在匹配的子节点中进行选择时,该函数必须返回一个节点箍向下/下一个/上一个更少的节点。这个要求还没有达到,但是这个功能还是挺灵活的,在你提供的例子的情况下,似乎可以做你想做的事情。

    【讨论】:

      【解决方案6】:

      我发现这段代码很简单,但没有解决平局问题(返回第一个)...

      (function ($) {
        $.fn.findClosest = function (filter) {
          var $found = $(),
                  $currentSet = this; // Current place
      
          while ($currentSet.length) {
              $found = $currentSet.find(filter);
              if ($found.length) break; // At least one match: break loop
              // Get all children of the current set
              $currentSet = $currentSet.parent();
          }
          return $found.first(); // Return first match of the collection
        };
      })(jQuery);
      

      【讨论】:

      • 我的代码更简单,并且与您尝试实现的功能相同(如果我正确理解您想要实现的目标)。
      • 当然,除非您需要任意过滤器。我的只适用于可以写为选择器的过滤器。
      • 是的,你的更简单,我非常喜欢“保持简单”的方法,因此被标记为正确答案。我仍然发布了这个,请记住,后者我可以改进逻辑,以防出现平局以更少的箍返回关闭节点。但是现在这两种解决方案都对我有用,因为标记没有联系。
      【解决方案7】:

      我遇到了类似的问题,我有一个表我需要找到可能在当前 td 之外的下一个元素,所以我做了一个 jquery 函数:

          $.fn.nextAllLevels = function(sel) {
          if ($(this).nextAll(sel).length != 0) {
              return $(this).nextAll(sel).eq(0);
          } else if ($(this).nextAll(':has(' +  sel + ')').length != 0) {
              return $(this).nextAll(':has(' +  sel + ')').find(sel).eq(0);
          } else {
              return $(this).parent().nextAllLevels(sel);
          }
      

      所以要使用它,您只需调用

      $('#current').nextAllLevels('.target');
      

      为您提供最接近前向的元素,无论 in 是否在当前父级中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-27
        • 1970-01-01
        • 2012-09-28
        • 2019-06-28
        • 2023-03-20
        • 1970-01-01
        相关资源
        最近更新 更多