jQuery: use filter(), but work with both results
In jQuery, filter()
reduces your result to those elements that fulfill a certain condition.
This splits the list in two parts. Working with the "good half" of the elements is easy:
$("some selector").filter(function() {
// determine result...
return result;
}).each( /* do something */ );
But how can I work with the "other half" of my elements, too - but without doing the equivalent of this:
$("some selector").filter(function() {
// determine result...
return !result;
}).each( /* do something else */ );
Basically, I'd like to feed two separate /* do something */
parts to a single filter. One for those that match, and one for the others - without having to filter twice. Am I missing a jQuery function that does this?
P.S.: I guess I could do:
$("some selector").each(function() {
// determine result...
if (result)
/* do something */
else
/* do something els开发者_运维技巧e */
});
But I was hoping for something nicer.
The method recommended by Kobi in plugin form:
$.fn.invert = function() {
return this.end().not(this);
};
$('.foo').filter(':visible').hide().invert().show();
Note that invert()
will not add a new element to the jQuery stack but replace the last one:
$('.foo').filter(':visible').invert().end(); // this will yield $('.foo'), not $('.foo:visible')
Edit: changed prevObject
to end()
at Tomalak's suggestion.
I usually use not
for this - it can take an array of elements and remove them from your selection, leaving you with the complement:
var all = $("some selector");
var filtered = all.filter(function() {
// determine result...
return result;
});
var others = all.not(filtered);
You might try your hand at writing a jQuery plugin to do this. Check out the code of the filter function, and come up with something that does more precisely what you want. It could be something like:
$("some selector").processList(predicate, successCallback, failureCallback);
Then you would pass in three callbacks: one that evaluates an object to see if it matches the filter selection (you could also accept a selector string, or the like); one that handles objects that match the selection, and another that handles objects which don't match.
$.fn.if = function(cond, ontrue, onfalse) {
this.each(function() {
if (cond.apply(this)) ontrue.apply(this);
else onfalse.apply(this);
});
};
$('some selector').if(function() {
// determine result
}, function() {
// do something
}, function() {
// do something else
});
I'm not sure it is much more readable than putting an if inside an each manually, though.
I don't know if this is any nicer, but using filter()
you could do something like:
var $others = $();
var $filtered = $('div').filter(function() {
if(! your filter test) {
$others.push(this);
} else {
return true;
}
});
alert($others.length);
alert($filtered.length);
EDIT:
At first I tried it starting with an empty jQuery set $()
, and then using add()
to populate it with the non-filter results, but couldn't make it work.
EDIT:
Updated to use push directly on an empty jQuery object as suggested by Tomalak.
Interesting question. I see you are leaning toward what I was going to suggest:
$("some selector").each(function() {
if ($(this).is(SOMEFILTER)) {
// do something
} else {
// do something
}
// continue with things that apply to everything
});
精彩评论