jquery filtering has + not
Solution 1
You can use a combination of the :not and :has selectors like this
$("a.add-span").click(function() {
$("li:not(:has(span))").each(function(index) {
$(this).append('<span class="spn">new span<\/span>');
});
});
Here is a demo http://www.jsfiddle.net/xU6fV/
Solution 2
$("a.add-span").click(function() {
$("li").each(function(index) {
if ($(this).children('span').length === 0){
$(this).append('<span class="spn">new span<\/span>');
}
})
})
With children()
method, the length
property is used to check whether or not a span
already exits and if it doesn't, one is added/appended.
More Info:
Solution 3
This is the first link on Google when searching "jquery not has". The specific scenario I needed to solve was a little different to this one. I had to not
items that have a specific DOM element, not simply based on a tag. This meant I couldn't use a selector which each solution above used.
jQuery's has()
however does accept a DOM element! So I created a jQuery plugin to combine these two inbuilt functions.
I wanted to share it here because hopefully it will help others in my situation too. It also answers the original question.
The Plugin:
(function ( $ ) {
$.fn.extend({
not_has: function (param) {
return this.not(this.has(param));
}
});
}( jQuery ));
Implementation:
$("a.add-span").click(function() {
$("li").not_has("span")
.append('<span class="spn">new span<\/span>');
// Prevent the page from navigating away
return false;
});
https://jsfiddle.net/brjkg10n/1/
I was initially intending to find a jQuery function that would work like addBack()
but using not
instead. If you need something so complicated, then please feel free to use the following plugin.
(function ( $ ) {
$.fn.extend({
notBack: function () {
return this.prevObject.not(this);
}
});
}( jQuery ));
With this, the answer would be:
$("li").has("span").notBack()
.append('<span class="spn">new span<\/span>');
https://jsfiddle.net/2g08gjj8/1/
Solution 4
The accepted answer works well, if you don't want to filter out only those elements which do have the span
s as direct children.
As the :has()
and the .has()
loop over all descendants, not just the children.
In that case, you have to use a function
$("li").not(function() {
// returns true for those elements with at least one span as child element
return $(this).children('span').length > 0
}).each(function() { /* ... */ })
FFish
Updated on October 20, 2020Comments
-
FFish over 3 years
Okay I have list items, some have a span, some not.
On my event I want to add the span when they don't have any yet.has()
works fine, butnot()
adds the span to both??HTML:
<ul> <li> <p>item</p> <span class="spn">empty span</span> </li> <li> <p>item 2</p> </li> <ul> <hr> <a class="add-span"href="#">check</a>
JS:
$("a.add-span").click(function() { $("li").each(function(index) { // $(this).has("span").find("span").append(" - appended"); $(this).not("span").append('<span class="spn">new span<\/span>'); }) })
-
Jeff Rupert over 13 yearsDidn't you mean
if($(this).children('span') ...
? But otherwise, +1. Didn't even think of that method. =) -
Sarfraz over 13 years@Jeff Rupert: True, it is
children
, I did not focus on his selector. Thanks -
John Hartsock over 13 years@Sarfraz... wouldint it be easier to just specify in the selector for the each function...IE $("li:not(:has(span))").each(...)
-
Sarfraz over 13 years@John Hartsock: Agreed but it didn't come in my mind at the time of writing and that is better actually :)
-
John Hartsock over 13 years@FFish ... This is a cleaner solution
-
FFish over 13 yearsNice combination of not and has, at the end I choose Sarfraz option because I have another filter in the each loop, thanks!
-
FFish over 13 yearsThanks for digging this solution.
-
vipul sorathiya about 8 yearsi want reverse of it. is it possible?
-
John Hartsock about 8 years@vipulsorathiya you mean you want to find elements under an li that has a span tag inside?
-
John Hartsock about 8 years@vipulsorathiya should be $("li:has(span)")
-
vipul sorathiya about 8 years@JohnHartsock thnks a lot...!!
-
WoodrowShigeru about 7 yearsNote: it doesn't work in the reverse order (first
has
, thennot
). It has to be:not(:has(xyz))
. -
John Hartsock about 7 years@WoodrowShigeru Not sure what your suggesting. but the above does facilitate an answer to OP's original question.
-
WoodrowShigeru about 7 yearsI'm just pointing out that the order is important, not arbitrary. Your answer is fine, I'm just adding to it (for people who like to experiment).