问题1:jQuery.filter的源码是什么?
jQuery.filter = function( expr, elems, not ) { var elem = elems[ 0 ]; //如果含有第三个参数表示选择不满足特定条件的结果! if ( not ) { expr = ":not(" + expr + ")"; } return elems.length === 1 && elem.nodeType === 1 ? //如果选择集合只有一个元素,调用matchesSelector,否则调用matches方法! jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; })); //很显然返回的是集合! };很显然返回的是对象数组,同时第三个参数表示是否取反,grep第三个参数也是取反功能!
问题2:jQuery.filter调用方式有那些?
在prevAll,nextAll,siblings中调用
ret = jQuery.filter( selector, ret );例子:
<div class="father" id="father" style="background-color:red;height:400px;background-color:#ccc;"> </div> <div class="father" style="background-color:red;height:400px;background-color:#ccc;"> </div>JS部分
//获取集合$("div")中满足class为father的元素 var result=jQuery.filter(".father",$("div")); console.log(result);//打印[div#father.father, div.father]因为jQuery.filter的第三个参数表示 是否取反,那么我们如何把它设置为true表示取反,也就是不满足selector的元素的集合
//获取集合$("div")中满足class不为father的元素 var result=jQuery.filter(".father",$("div"),true); console.log(result);//打印[]通过源码很容易看到
if ( not ) { expr = ":not(" + expr + ")";//第三个参数表示对选择器进行取反,也就是添加:not! }注意:jQuery.filter是对元素的集合进行筛选,所以返回的数组元素本来就在筛选之前的集合中!
学习实例filter方法之前,我们先看看winnow方法
他的调用方式有以下
实例filter方法中:
winnow(this, selector || [], false)实例not方法中:
winnow(this, selector || [], true)实例is方法中:
winnow(this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ?jQuery( selector ) :selector || [],false)现在我们看看winnow方法的源码:
function winnow( elements, qualifier, not ) { if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; }); } if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; }); } if ( typeof qualifier === "string" ) { if ( risSimple.test( qualifier ) ) { return jQuery.filter( qualifier, elements, not ); } qualifier = jQuery.filter( qualifier, elements ); } return jQuery.grep( elements, function( elem ) { return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; }); }如果是实例not,filter方法(传入字符串),那么执行下面的逻辑
//在filter和not实例方法中,执行这里的逻辑 if ( typeof qualifier === "string" ) { //var risSimple = /^.[^:#\[\.,]*$/; //如果是普通的选择器,那么我们用这个selector对数组进行筛选 //对于not方法来说传入的true表示取反,而is实例方法不需要取反! if ( risSimple.test( qualifier ) ) { return jQuery.filter( qualifier, elements, not ); } qualifier = jQuery.filter( qualifier, elements ); }所以还是调用jQuery.filter对 调用对象进行筛选,所以返回的对象 都是调用对象的一个子集!
<div class="father" id="father" style="background-color:red;height:400px;background-color:#ccc;"> </div> <div class="father" style="background-color:red;height:400px;background-color:#ccc;"> </div>我们给filter传入DOM
//我们知道is,not,filter都是可以传入DOM对象的 var result=$("div").filter($("#father")[0]); console.log(result); //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]传入DOM,表示筛选指定的DOM元素,这时候返回的是含有指定DOM的jQuery对象
给is方法传入DOM
//我们知道is,not,filter都是可以传入DOM对象的 var result=$("div").is($("#father")[0]); console.log(result); //打印true!这时候打印true,只要调用对象含有参数对象,那么返回 布尔值true!
//我们知道is,not,filter都是可以传入DOM对象的 var result=$("div").not($("#father")[0]); console.log(result); //打印[div.father, prevObject: jQuery.fn.init[2], context: document]!这时候打印第二个div元素,表示返回除了指定元素外的其它的元素! 要注意:is方法返回的是布尔值!
if ( qualifier.nodeType ) { //调用了grep方法,该方法为每一个元素调用指定的函数,如果该函数返回true //那么把该元素放入结果集合中(grep第三个参数在这里是false!)。 return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; }); }如果是is或者filter方法,那么not是false,表示不用取反,这时候在上面这段代码中就是返回elem===qualifier的对象,也就是传入的参数对象,不同之处在于is已经转化为布尔值了 ! jQuery.grep的作用在于:选择调用指定函数后返回值是我们期望内向的元素(如果第三个参数是false表示返回调用函数返回true的元素,如果第三个参数是true表示返回调用函数的结果是false的元素)。如果是not方法,那么返回调用对象中不是指定元素的DOM元素!
问题5:is,not,filter方法中可以传入jQuery对象?
HTML还是和上面的例子一样
not实例方法
//我们知道is,not,filter都是可以传入jQuery对象的 var result=$("div").not($("#father")); console.log(result); //打印[div.father, prevObject: jQuery.fn.init[2], context: document]is方法
//我们知道is,not,filter都是可以传入jQuery对象的 var result=$("div").is($("#father")); console.log(result); //打印truefilter实例方法
//我们知道is,not,filter都是可以传入jQuery对象的 var result=$("div").filter($("#father")); console.log(result); //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]这时候在winnow方法中执行下面的代码逻辑:
return jQuery.grep( elements, function( elem ) { return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; });这时候jQuery.grep第三个参数是false,表示返回执行指定函数后为true的DOM元素组成的数组。对于is,filter方法来说,not是false,那么表示,如果当前元素在参数也就是qualifier所在的集合中就会返回! 反过来想想,只要调用对象在参数集合中都会返回!如果是not实例方法,不在参数jQuery对象中的调用对象的所有的DOM都会返回!
//我们知道is,not,filter都是可以传入函数对象的 var result=$("div").filter(function(index,elem) { if(elem.id==="father") { return false; //不需要放入结果集合中就直接返回false,所以id=father元素不会在结果集合中 } return true;//需要返回就return true! }); //打印[div.father, prevObject: jQuery.fn.init[2], context: document] console.log(result);注意:传入的回调函数情况下,回调函数中第一个参数是下标,第二个参数是元素!
//我们知道is,not,filter都是可以传入函数对象的 var result=$("div").is(function(index,elem) { if(elem.id==="father") { return false; //不需要放入结果集合中就直接返回false,所以id=father元素不会在结果集合中 } return true;//需要返回就return true! }); //打印true,因为对于第一个div元素执行函数返回了true! console.log(result);只要有一个函数调用后返回了true,那么结果就是true!
//我们知道is,not,filter都是可以传入函数对象的 var result=$("div").not(function(index,elem) { if(elem.id==="father") { return false; //not方法如果返回false那么会在结果集合中! } return true;//返回true不再结果集合中! }); //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document] console.log(result);not方法传入函数,只要返回false就会在结果集合中,返回true不会在结果集合中!执行的代码逻辑是
if ( jQuery.isFunction( qualifier ) ) { //如果是函数参数,那么我们让每一个元素执行指定的函数! return jQuery.grep( elements, function( elem, i ) { /* jshint -W018 */ return !!qualifier.call( elem, i, elem ) !== not; }); }实例not方法源码
not: function( selector ) { return this.pushStack( winnow(this, selector || [], true) ); }实例is方法源码
is: function( selector ) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ? jQuery( selector ) : selector || [], false ).length; }实例filter源码
filter: function( selector ) { return this.pushStack( winnow(this, selector || [], false) );//通过winnow方法选择成功过后我们进行入栈,返回是jQuery对象! }问题7:我们不是讲实例filter方法和实例find方法的差别吗?看下面的差别?
从这里你就会看到:虽然find方法和其它的方法一样可以传入DOM,jQuery,字符串(find方法无法传入函数),但是find方法返回的是调用对象满足指定选择器的子元素,而filter方法返回的是调用对象的一个集合!
jQuery.find = Sizzle;我们知道最终还是调用了Sizzle的相关方法
var i, ret = [], self = this, len = self.length; //如果find方法传入DOM,jQuery等执行这里的逻辑 if ( typeof selector !== "string" ) { //所以如果是DOM,jQuery最终还是调用了filter实例方法 //不过,这时候成了判断调用对象是否包含参数对象,如果包含就直接返回! //记住:这时候返回的是参数对象,参数对象扮演了子元素的角色 return this.pushStack( jQuery( selector ).filter(function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } }) ); }通过上面的代码,虽然find方法不能传入函数,但是如果传入了DOM/jQuery对象,那么参数扮演的就是子元素的角色, 该方法返回的是子元素!
var result=$("div").find($("#father")); //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document] //返回的是子元素! console.log(result);判断$("div")这个调用对象集合中是否有子元素的id=father,如果有,那么返回!
//如果是字符串对象的选择器,调用Sizzle,把所有的结果放入ret集合中! for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } // Needed because $( selector, context ) becomes $( context ).find( selector ) //如果调用对象的DOM对象有多个,那么调用jQuery.unique来对集合进行去重,如果只有一个DOM不用去重! //这时候返回的ret已经是jQuery对象了! ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); //这时候要对selector进行更新,$("div").find(".name")那么返回的对象就是通过双重选择来完成的 //变成("div .name") ret.selector = this.selector ? this.selector + " " + selector : selector; return ret;注意:通过find方法返回的是jQuery对象。我们看看下面的代码:
ret.selector = this.selector ? this.selector + " " + selector : selector;通过下面的代码:
var result=$("div").find($("#father")); //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document] //返回的是子元素! console.log(result);这时候返回的对象result的selector是"div#father"也就是双重选择的结果了!
总结:
(1)filter方法把this对象也就是调用对象传入winnow函数,同时把参数也就是选择器也传入winnow,在winnow里面调用jQuery.filter按照指定的选择器进行筛选,返回的是调 用对象个部分元素
(2)对于find方法,如果传入的对象不是string,如DOM对象等,那么通过contains或者compareDocumentPosition来判断。其中参数对象扮演的角色就是子元素,只要有调用对象包含着这个参数对象,就会把参数对象保存起来,所以该方法保存的是子元素!
(3)区别:filter返回调用对象自己,find方法返回子元素