如果 div 包含所有指定的元素,请选择它



我正在尝试选择包含tagOnetagTwo span元素的.itemdiv。

我的div结构如下:

<div id="items">
    <div id="block1" class="item">
        <span class="tagOne tag">One</span>
        <span class="tagTwo tag">Two</span>
    </div>
    <div id="block2" class="item">
        <span class="tagOne tag">Java</span>
    </div>
</div>

使用以下jQuery,我能够分别找到标签(及其父div

)。
var blocks =  $('#items .item');
blocks.filter('.item').find('[class*="tagOne"]').parent();
blocks.filter('.item').find('[class*="tagTwo"]').parent();

但是,一旦我尝试将它们组合起来以将其缩小到包含它们div,我没有得到任何结果,我似乎无法弄清楚为什么!

blocks.filter('.item').find('[class*="tagOne"][class*="tagTwo"]');

我的理解是逗号语法将创建一个 OR 表达式,而不创建 AND 表达式。我在 AND 表达式之后,因为我只想返回包含所有条件的div,或者什么都不想返回。


注意:我这样做是因为我正在根据标签和条件(即 tagOne, tagTwo ) 是用户选择的标记(未显示)的串联,因此最好尝试在一个操作中执行此操作。


编辑:将重复的id移动到class名称,以使其有效并相应地调整JavaScript代码。

首先,ID 应该是唯一的。现在,标记包含两个 ID tagOne 的元素,这是无效标记。

您可以使用类而不是 ID。

  1. 从两个元素中选择任何一个(在本例中为 .tagOne.tagTwo
  2. 使用 siblings() 选择具有其他类的同级元素
  3. 使用 closest() 选择与选择器匹配的最接近的祖先。

上面的步骤 #1、#2 和 #3 将仅选择那些同时具有 .tagOne.tagTwo 作为后代的.item元素。

法典:

$('.tagOne') // Select one of the element
  .siblings('.tagTwo') // Get second element if it is sibling
  .closest('.item') // Get the closest ancestor

$('.tagOne') // Select one of the element
  .siblings('.tagTwo') // Get second element if it is sibling
  .closest('.item') // Get the closest ancestor
  .addClass('selected'); // For Demo purpose
.item {
  color: red;
}
div.selected {
  color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
  <div id="block1" class="item">
    <span class="tagOne tag">One</span>
    <span class="tagTwo tag">Two</span>
  </div>
  <div id="block2" class="item">
    <span class="tagOne tag">Java</span>
  </div>
  <div id="block3" class="item">
    <span class="tagTwo tag">I Love JavaScript</span>
  </div>
</div>


您也可以按如下方式使用filter

  1. 使用filter()遍历所有.item元素
  2. 使用上下文选择器检查当前.item是否具有后代.tagOne.tagTwo
  3. 使用 jQuery 对象上的length属性来获取选择器选择的元素数。

法典:

$('.item').filter(function() {
  return $('.tagOne', this).length && $('.tagTwo', this).length;
})

// Fiddle: https://jsfiddle.net/tusharj/8tuu1wxs/1/
// Iterate over all elements having item class
$('.item').filter(function() {
  return $('.tagOne', this).length && $('.tagTwo', this).length;
}).addClass('selected');
.item {
  color: red;
}
.selected {
  color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
  <div id="block1" class="item">
    <span class="tagOne tag">One</span>
    <span class="tagTwo tag">Two</span>
  </div>
  <div id="block2" class="item">
    <span class="tagOne tag">Java</span>
  </div>
  <div id="block3" class="item">
    <span class="tagTwo tag">I Love JavaScript</span>
  </div>
</div>


如果元素的顺序/顺序是固定的,则可以使用 CSS 通用同级选择器~或相邻同级选择器+

$('.tag1 ~ .tag2').closest('.item')

$('.tag1 + .tag2').closest('.item')

// Fiddle: https://jsfiddle.net/tusharj/amdoLfou/1/
$('.tag1 ~ .tag2') // + can also be used instead of ~
  .closest('.item') // Get closest ancestor
  .css('color', 'blue'); // For Demo purpose
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
  <div id="block1" class="item">
    <span class="tag1 tag">One</span>
    <span class="tag2 tag">Two</span>
  </div>
  <div id="block2" class="item">
    <span class="tag1 tag">Java</span>
  </div>
</div>

虽然你已经接受了答案,但我觉得这是一个值得使用普通JavaScript的问题,而不仅仅是jQuery解决方案。因此,考虑到这一点,我想提供以下方法(它确实使用了一些 ECMAScript 6 盛宴,因此确实需要一个相当现代的浏览器):

// using an Immediately-Invoked Function Expression syntax,
// so that the enclosed function will be executed when
// encountered, rather than requiring the user to call it
// explicitly (this would need to run in a DOMReady callback
// or once the DOM has been constructed, however):
(function hasAll(opts) {
  // setting the default settings for the function:
  var settings = {
    // a CSS Selector string to identify the ancestor
    // element that you wish to identify:
    'ancestorSelector': 'div',
    // an array of CSS Selectors to identify the
    // descendants by which the ancestor should
    // be found:
    'descendantSelectors': []
  }
  // looking at the named (not inherited) properties
  // of the opts Object supplied by the user:
  for (var property in opts) {
    // if the opts Object has a given property
    // name we set the corresponding property
    // of the settings Object to be equal to that
    // property-value:
    if (opts.hasOwnProperty(property)) {
      settings[property] = opts[property];
    }
  }
  // finding all the elements represented by the first selector
  // of the user-supplied selectors contained within an element
  // matching the ancestor selector:
  var firstElements = document.querySelectorAll(
      settings.ancestorSelector + ' ' + settings.descendantSelectors[0]
    ),
  // converting the NodeList returned by document.querySelectorAll()
  // into an Array, using Array.from:
    arrayOfFirsts = Array.from(firstElements),
  // here we iterate over that Array, using Array.prototype.filter():
    hasSiblings = arrayOfFirsts.filter(function(n) {
      // we look for the parentNode of the current node (n):
      var p = n.parentNode;
      // we use Array.prototype.every() to ensure that every
      // selector in the descendantSelectors Array returns
      // a Node (document.querySelector() returns only the
      // first node matching the given selector, or null if
      // there is no element matching that selector).
      // if Array.prototype.every() returns true (all elements
      // of the Array match the supplied test) then the current
      // node (n) is retained in the array returned by
      // Array.prototype.filter():
      return settings.descendantSelectors.every(function(selector) {
        // Array.prototype.every() returns a Boolean,
        // true : if all elements of the Array match
        //        the supplied test/assessment,
        // false: if *any* of the elements of the Array
        //        fail to match.
        // this is the test that we're matching against:
        return p.querySelector(selector) !== null;
      });
    });
  // here we iterate over the hasSiblings Array, and use
  // Array.prototype.map() to form a new Array, using
  // an Arrow function to take the current node (n)
  // and find, and return, the closest element to that
  // node which matches the supplied settings.ancestorSelector:
  var found = hasSiblings.map(n => n.closest(settings.ancestorSelector));
  // returning that array to the calling context:
  return found;
})({
  // this is the 'opts' Object that we're passing to the
  // IIFE-contained function:
  'ancestorSelector': '.item',
  'descendantSelectors': ['.tagOne', '[data-demo]']
// using Array.prototype.forEach() to iterate over the
// returned elements, to add the class 'hasAll' to the
// the classList (the list of class-names) of the given
// node (n):
}).forEach(n => n.classList.add('hasAll'));

(function hasAll(opts) {
  var settings = {
    'ancestorSelector': 'div',
    'descendantSelectors': []
  }
  for (var property in opts) {
    if (opts.hasOwnProperty(property)) {
      settings[property] = opts[property];
    }
  }
  var firstElements = document.querySelectorAll(
      settings.ancestorSelector + ' ' + settings.descendantSelectors[0]
    ),
    arrayOfFirsts = Array.from(firstElements),
    hasSiblings = arrayOfFirsts.filter(function(n) {
      var p = n.parentNode;
      return settings.descendantSelectors.every(function(selector) {
        return p.querySelector(selector) !== null;
      });
    });
  var found = Array.from( hasSiblings.map(n => n.closest(settings.ancestorSelector)) );
  return found;
})({
  'ancestorSelector': '.item',
  'descendantSelectors': ['.tagOne ~ .tagTwo']
}).forEach(n => n.classList.add('hasAll'));
div {
  width: 50%;
  margin: 0.5em auto;
  border: 2px solid #000;
  border-radius: 1em;
  padding: 0.5em;
  box-sizing: border-box;
}
.hasAll {
  border-color: #f90;
}
.hasAll span {
  color: #f90;
  font-weight: bold;
}
<div id="items">
  <div id="block1" class="item">
    <span class="tag tagOne">One</span>
    <span class="tag tagTwo">Two</span>
  </div>
  <div id="block2" class="item">
    <span class="tag tagOne">Java</span>
  </div>
  <div id="block3" class="item">
    <span class="tag tagOne" data-demo="false">tag-one</span>
    <span class="tag tagTwo">tag-two</span>
    <span class="tag" data-demo="true">tag-three</span>
  </div>
</div>

JS小提琴演示。

请注意,使用上述函数,如果祖先元素的任何后代或其后代的兄弟姐妹匹配多个选择器,则祖先元素将被匹配。

引用:

  • Array.from() .
  • Array.prototype.every() .
  • Array.prototype.filter() .
  • Array.prototype.forEach() .
  • Array.prototype.map() .
  • 箭头函数。
  • document.querySelector() .
  • document.querySelectorAll() .
  • Element.classList .
  • Element.closest() .
  • for...in声明。
  • Object.hasOwnProperty() .
  • Node.parentNode .

试试这个

 blocks.filter('.item').find('[id="tagOne"],[id="tagTwo"]');

尝试使用 jquery 有选择器has它会搜索选定的节点是否有某些子节点:https://jsfiddle.net/96gbf7xg/

最新更新