是否有一种简单的方法在JQuery中执行两个节点集之间的减法

Is there a simple way to perform subtraction between two nodesets in JQuery?

本文关键字:两个 节点 之间 执行 点集 一种 简单 JQuery 是否 方法      更新时间:2023-09-26

请注意:这个问题与我在这里发现的其他一些问题非常相似,但它是不重复,所以请仔细阅读:

本质上,我想通过类名从集合中排除节点及其所有后代。

我所做的扩展(检查片段)在给定的例子中完美的工作,但我愿意找到更容易/更优雅的方式来做到这一点(如果它目前存在于jquery中)。

我正在寻找一个通用的方式来执行$(this).find('<some_selector>').exclude_found_nodes_that_have_the_following_between_aforementioned_this_and_itself('<exclusion_selector>'),然而像$(this).find('<exclusion_selector>:ignore_branch_if_this_node_met() <some_selector>')这样的东西也可以做这项工作。

id实际上并没有出现在代码中,添加它们纯粹是为了演示目的("起始节点"由用户选择)。

$.fn.find_exclude = function(original_selector,exclude_selector) {
  var counter=$(this).parents(exclude_selector).length;
  if ($(this).is(exclude_selector)){
    counter++;
  }
  var tmp=$(this).find(original_selector); 
  return tmp.filter(function(){
      if($(this).is(exclude_selector) || $(this).parents(exclude_selector).length>counter){
        return false;
    }
      return true
    });
};
$(function(){
  var all_b_in_main_not_in_groups=$("#main").find_exclude('b','.group');
  var all_b_in_inner_not_in_groups=$("#inner").find_exclude('b','.group');
  
  all_b_in_main_not_in_groups.each(function(){
    $(this).css('color','red');
  });
  all_b_in_inner_not_in_groups.each(function(){
    $(this).css('opacity','0.5');
  });
  
  
  
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<span id="main">
<b>I should be red</b>
<div class="group" id="inner">
  <span>
    <b>I should be opaque but not red</b>
  </span>
</div>
<div>
  <b>I should be red</b>
  <div class="group">
     <b>Don't touch me, please.</b>
        <div>
          <b>Don't touch me, please.</b>
            <p>
              <b>Don't touch me, please.</b>
            </p>
        </div>
  </div>
</div>
</span>

警告:在建议只使用:not.not(),甚至一些使用.clone()之前,请再次重读问题和给定的示例,并检查您的解决方案是否真的有效-建议的解决方案应与#main#inner一起工作作为启动节点(实际上不知道启动节点选择器-它们被称为$(this)click事件侦听器)

function customFind (findWhere, findWhat, ignoreWhat){
    var totalIgnore = $(findWhere).find(ignoreWhat + ' ' + findWhat);
    return $(findWhere).find(findWhat).not(totalIgnore);
}

使用:

var zzz= customFind(this,"b",".group");
zzz.css('text-decoration','underline');

您可以使用以下插件版本。它使用了.not()可以接受另一个jQuery选择的事实:

$.fn.find_exclude = function(original_selector, exclude_selector) {
    return $(this).find(original_selector).not(exclude_selector)
            .not($(this).find(exclude_selector).find(original_selector));
}
$(function(){
  var all_b_in_main_not_in_groups = $("#main").find_exclude('b','.group');
  all_b_in_main_not_in_groups.css('color', 'red');
  var all_b_in_inner_not_in_groups = $("#inner").find_exclude('b','.group');
  all_b_in_inner_not_in_groups.css('opacity', '0.5');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="main">
    <b>I should be red</b>
    <div class="group" id="inner">
      <span>
        <b>I should be opaque but not red</b>
      </span>
    </div>
    <div>
      <b>I should be red</b>
      <div class="group">
         <b>Don't touch me, please.</b>
            <div>
              <b>Don't touch me, please.</b>
                <p>
                  <b>Don't touch me, please.</b>
                </p>
            </div>
      </div>
    </div>
</span>

最后一个.not()是主要部分:它排除了要排除的父节点的后代节点。如果节点同时满足原始选择器和排除选择器,则不会被此.not()排除,因此将应用另一个.not()(第一个)来处理这些情况。

与您的问题无关,但请注意,您不需要.each()循环来应用.css()方法调用。你可以直接把它应用到jQuery的选择中。

备选:不搜索排除的子树

在评论中,你问是否有可能执行不需要首先通过原始选择器匹配所有节点的搜索,然后删除那些应该根据排除选择器排除的节点。

这可以通过使用递归函数调用(深度优先搜索)执行节点搜索来实现,同时在到达必须排除的节点时省略递归搜索:

$.fn.find_exclude = function(original_selector, exclude_selector) {
    if ($(this).length == 0) return $();
    var $elems = $(this).children().not(exclude_selector);
    return $elems.filter(original_selector)
                 .add($elems.find_exclude(original_selector, exclude_selector));
}

请注意,DOM API在使用选择器查找节点方面相对较快(参见jQuery使用的.querySelectorAll),因此使用这种替代方法"自己"执行会带来一些开销,最终可能比使用第一种方法时花费更多的时间。这取决于待排除节点下面的子树有多大。它们越大,替代递归方法的性能越好。但我猜在"正常"情况下,第一个解决方案会更快。