开发者

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
}); 
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜