Select a div 如果它包含所有指定的元素
Select a div if it contains all specified elements
我正在尝试 select 包含 tagOne
和 tagTwo
span
元素的 .item
div。
我的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 表达式之后,因为我只想 return 包含所有条件的 div,或者什么都没有。
注意:我这样做是因为我正在创建一个基于标签的切换过滤器,并且标准(即 tagOne, tagTwo
)是标签 select 的串联由用户编辑(未显示),因此最好尝试在一次操作中完成。
编辑: 将重复的 id
移动到 class
名称而不是使其有效并相应地调整 JavaScript 代码。
试试这个
blocks.filter('.item').find('[id="tagOne"],[id="tagTwo"]');
首先,ID 应该是唯一的。现在,标记包含两个 ID 为 tagOne
的元素,这是无效标记。
您可以使用 class 代替 ID。
- Select 两个元素中的任何一个(
.tagOne
或 .tagTwo
在这种情况下)
- 使用
siblings()
到 select 具有另一个 class 的兄弟元素
- 使用
closest()
到 select 最近的祖先匹配 selector.
上面的步骤 #1、#2 和 #3 将 select 只有那些同时具有 .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
如下。
- 使用
filter()
遍历所有 .item
元素
- 使用context selector检查当前
.item
是否有后代.tagOne
和.tagTwo
。
- 在 jQuery 对象上使用
length
属性 来获取由 selector. 编辑的元素数 select
代码:
$('.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>
如果元素的sequence/order是固定的,CSSgeneral sibling selector ~
or adjacent sibling selector +
就可以了
$('.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>
尝试使用 has
jquery has selector 它搜索所选节点是否有某些子节点:
https://jsfiddle.net/96gbf7xg/
虽然您已经接受了一个答案,但我觉得这个问题值得一个简单的 JavaScript,而不是简单的 jQuery 解决方案。因此,考虑到这一点,我想提供以下方法(它确实使用了一些 ECMAScript 6 features,因此确实需要一个相当现代的浏览器):
// 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>
请注意,使用上述函数,如果祖先元素的任何后代或其后代的兄弟元素与多个选择器匹配,则该祖先元素将被匹配。
参考文献:
我正在尝试 select 包含 tagOne
和 tagTwo
span
元素的 .item
div。
我的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 表达式之后,因为我只想 return 包含所有条件的 div,或者什么都没有。
注意:我这样做是因为我正在创建一个基于标签的切换过滤器,并且标准(即 tagOne, tagTwo
)是标签 select 的串联由用户编辑(未显示),因此最好尝试在一次操作中完成。
编辑: 将重复的 id
移动到 class
名称而不是使其有效并相应地调整 JavaScript 代码。
试试这个
blocks.filter('.item').find('[id="tagOne"],[id="tagTwo"]');
首先,ID 应该是唯一的。现在,标记包含两个 ID 为 tagOne
的元素,这是无效标记。
您可以使用 class 代替 ID。
- Select 两个元素中的任何一个(
.tagOne
或.tagTwo
在这种情况下) - 使用
siblings()
到 select 具有另一个 class 的兄弟元素
- 使用
closest()
到 select 最近的祖先匹配 selector.
上面的步骤 #1、#2 和 #3 将 select 只有那些同时具有 .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
如下。
- 使用
filter()
遍历所有 - 使用context selector检查当前
.item
是否有后代.tagOne
和.tagTwo
。 - 在 jQuery 对象上使用
length
属性 来获取由 selector. 编辑的元素数 select
.item
元素
代码:
$('.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>
如果元素的sequence/order是固定的,CSSgeneral sibling selector ~
or adjacent sibling selector +
就可以了
$('.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>
尝试使用 has
jquery has selector 它搜索所选节点是否有某些子节点:
https://jsfiddle.net/96gbf7xg/
虽然您已经接受了一个答案,但我觉得这个问题值得一个简单的 JavaScript,而不是简单的 jQuery 解决方案。因此,考虑到这一点,我想提供以下方法(它确实使用了一些 ECMAScript 6 features,因此确实需要一个相当现代的浏览器):
// 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>
请注意,使用上述函数,如果祖先元素的任何后代或其后代的兄弟元素与多个选择器匹配,则该祖先元素将被匹配。
参考文献: