首頁 >web前端 >js教程 >Jquery 1.9.1源碼分析系列篩選操作

Jquery 1.9.1源碼分析系列篩選操作

PHPz
PHPz原創
2016-05-16 15:28:171135瀏覽

廢話不多說了直接奔入主題了。

jQuery.fn.find( selector )

find接受一個參數表達式selector:選擇器(字串)、DOM元素(Element)、jQuery物件。分兩種情況處理:

第一種,如果傳入的參數是非字串,則先透過jQuery選擇器將selector找出來,然後過濾出包含於目前jQuery物件所符合的元素的節點。

if ( typeof selector !== "string" ) {
 self = this;
 return this.pushStack( jQuery( selector ).filter(function() {
  for ( i = 0; i < len; i++ ) {
   if ( jQuery.contains( self[ i ], this ) ) {
    return true;
   }
  }
 }) );
}

可以看出過濾條件中jQuery.contains( self[ i ], this )是關鍵,該函數使用的是Sizzle選擇器中的函數,在Sizzle引擎中有分析,詳情點擊。

第二種,如果選擇器是字串,呼叫jQuery.find (= Sizzle)直接處理

ret = [];
for ( i = 0; i < len; i++ ) {
 //第二个参数是表示context
 jQuery.find( selector, this[ i ], ret );
}
//$( selector, context )变成$( context ).find( selector ),需要去重和pushStack
ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
ret.selector = ( this.selector ? this.selector + " " : "" ) + selector;
return ret;
jQuery.fn.closest( selectors, context )

第二個參數是可選的。函數用於從目前匹配元素開始,逐級向上級選取符合指定表達式的第一個元素,並以jQuery物件的形式傳回。

這裡的表達式包括:選擇器(字串)、DOM元素(Element)、jQuery物件。

程式碼中的處理步驟為

1.依照傳遞的參數先查詢出結果儲存在pos。

pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
jQuery( selectors, context || this.context ) :
0;

2.遍歷當前jQuery物件的每一個元素,從這個元素開始,逐級向上級選取符合指定表達式的第一個祖先元素。

for ( ; i < l; i++ ) {
 cur = this[i];
 while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
  if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
   ret.push( cur );
   break;
  }
  cur = cur.parentNode;
 }
}
return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );

parents() 和 .closest() 方法類似,它們都沿著 DOM 樹向上遍歷。但差異也很大closest找到第一個符合條件就截止,parents是找到所有符合條件的集合。

jQuery.fn. parent/ parents/ parentsUntil/ next/ prev/ nextAll/ prevAll/ nextUntil/ prevUntil/ siblings/children/ contents詳解

jQuery.each({
  parent: function( elem ) {…},
  parents: function( elem ) {…},
  parentsUntil: function( elem, i, until ) {…},
  next: function( elem ) {…},
  prev: function( elem ) {…},
  nextAll: function( elem ) {…},
  prevAll: function( elem ) {…},
  nextUntil: function( elem, i, until ) {…},
  prevUntil: function( elem, i, until ) {…},
  siblings: function( elem ) {…},
  children: function( elem ) {…},
  contents: function( elem ) {…}
 }, function( name, fn ) {
  jQuery.fn[ name ] = function( until, selector ) {
   var ret = jQuery.map( this, fn, until );
   //过滤
   ...
   return this.pushStack( ret );
  };
 });



if ( !runtil.test( name ) ) {
 selector = until;
}

if ( selector && typeof selector === "string" ) {
 ret = jQuery.filter( selector, ret );
}

ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;

if ( this.length > 1 && rparentsprev.test( name ) ) {
 ret = ret.reverse();
}

放在一起處理,原始碼如下
return this.pushStack( ret ); 


可以看出,這幾個篩選步驟一致。都是先透過map函數把當前jQuery物件每個匹配的元素代入對應的匹配函數(fn)中取得出結果然後在進行後續的過濾。

dir: function( elem, dir, until ) {
 var matched = [],
 cur = elem[ dir ];
 while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
  if ( cur.nodeType === 1 ) {
   matched.push( cur );
  }
  cur = cur[dir];
 }
 return matched;
},
//获取节点n及其兄弟节点中非elem的节点集合r
sibling: function( n, elem ) {
 var r = [];
 for ( ; n; n = n.nextSibling ) {
  if ( n.nodeType === 1 && n !== elem ) {
   r.push( n );
  }
 }
 return r;
}
//找到当前元素cur的下一个dir为止
function sibling( cur, dir ) {
  do {
   cur = cur[ dir ];
  } while ( cur && cur.nodeType !== 1 );
  return cur;
 } 
我們先看一下後面的過濾(已經透過jQuery.map( this, fn, until )取得到了備選種子ret)
首先,並不是所有的篩選函數都有until這個參數,只有幾個以Until結尾的篩選才需要這個參數,其他的篩選只有selector這個參數。
jQuery.fn.add( selector, context )和jQuery.fn. addBack( selector )

其次,如果有選擇器,則透過選擇器過濾先前查找結果ret
add: function( selector, context ) {
 var set = typeof selector === "string" ?
   jQuery( selector, context ) :
   jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
  //把selector表达式获取的结果集拼接到当前对象上
  all = jQuery.merge( this.get(), set );
 //返回新的拼接结果
 return this.pushStack( jQuery.unique(all) );
}
  jQuery.fn.add和jQuery.fn.not相对应。jQuery.fn.not后面再说。
  jQuery.fn.addBack将之前匹配的元素加入到当前匹配的元素中,并以新的jQuery对象的形式返回。
addBack: function( selector ) {
 return this.add( selector == null ?
  this.prevObject : this.prevObject.filter(selector)
  );
}
jQuery.fn.andSelf = jQuery.fn.addBack;
  
jQuery.fn.not( selector )和jQuery.fn.filter( selector )
not: function( selector ) {
 return this.pushStack( winnow(this, selector, false) );
}
filter: function( selector ) {
 return this.pushStack( winnow(this, selector, true) );
},

然後,guaranteedUnique裡面的幾種篩選條件(children/contents/next/prev)在目前jQuery物件所匹配的元素個數有多個的時候,透過每個匹配元素所獲得的結果會保存在結果集ret中,且不需要去重。其他篩選是要去重的。點擊查看jQuery.unique方法詳解

另外,還需要處理的特殊情況是: 如果當前jQuery物件所匹配的元素有多個,則使用parents /prevUntil / prevAll這三種篩選的結果需要倒序排列。需要倒序的原因:jQuery.unique使用的是Sizzle引擎中的排序函數Sizzle .uniqueSort,這個排序函數會依照文件最頂層物件到最底層的方式排列。
//执行相同的过滤或者不过滤的功能
function winnow( elements, qualifier, keep ) {
 // Can&#39;t pass null or undefined to indexOf in Firefox 4
 // Set to 0 to skip string check
 qualifier = qualifier || 0;
 //如果过滤条件是函数,则通过过滤函数过滤
 if ( jQuery.isFunction( qualifier ) ) {
  return jQuery.grep(elements, function( elem, i ) {
   var retVal = !!qualifier.call( elem, i, elem );
   return retVal === keep;
  });
 //如果过滤条件是DOM相关类型,通过比较节点是否相同来过滤
 } else if ( qualifier.nodeType ) {
  return jQuery.grep(elements, function( elem ) {
   return ( elem === qualifier ) === keep;
  });
 //如果过滤条件是字符串
 } else if ( typeof qualifier === "string" ) {
  //过滤出elements中的节点元素
  var filtered = jQuery.grep(elements, function( elem ) {
   return elem.nodeType === 1;
  });
  // 其中isSimple = /^.[^:#\[\.,]*$/
  if ( isSimple.test( qualifier ) ) {
   return jQuery.filter(qualifier, filtered, !keep);
  } else {
   //查找filtered中满足筛选条件qualifier的节点
   qualifier = jQuery.filter( qualifier, filtered );
  }
 }
 //过滤出elements中满足过滤条件的元素
 return jQuery.grep(elements, function( elem ) {
  return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
 });
}

最後,回包裹後的結果

jQuery.filter: function( expr, elems, not ) {
 if ( not ) {
  expr = ":not(" + expr + ")";
 }
 //其中matchesSelector和matches是Sizzle中的函数。matchesSelector是判断单个元素elem是否满足表达式expr,matches是查找元素集合elems中满足表达式expr的项
 return elems.length === 1 ?
  jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
  jQuery.find.matches(expr, elems);
},

jQuery.fn.index( elem )
上面說了主題的框架結構,下面說一下這一群組篩選器匹配函數裡面用到的兩個函數jQuery.dir和jQuery. sibling,直接上源碼//從當前元素elem指定的dir對應的節點開始一直查找dir,並將這些節點保存在matched中,直到循環終止。 注意:結果中不包含elem節點add函數是將指定表達式的元素,並以jQuery物件的形式傳回。 add可以接收包括:選擇器(字串)、HTML內容(字串)、DOM元素(Element)、jQuery物件。處理比較簡單,直接上源碼not和filter都是操作本身的集合,not是過濾掉本身集合中滿足過濾條件selector的項,留下其他項。而filter是留下滿足過濾條件selector的項。 關鍵是function winnow( elements, qualifier, keep )函數。這個函數的功能是執行相同的過濾或不過濾的功能。過濾條件qualifier有三種:函數、DOM節點、字串。 keep:true表示保留符合過濾條件的項,false表示保留不符合過濾條件的項。 winnow的源碼註解如下其中用到jQuery.grep,grep詳解點這裡。 jQuery.filter( expr, elems, not )這個低階api專門用來處理jQuery.fn.filter中篩選條件為字串的情況。

index函数实际上是一个多功能函数的集合。

第一个功能:不传递elem参数,则表示取当前jQuery对象(jQuery对象的第一个元素)在其所有同辈元素中的位置。

if ( !elem ) {
 return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
}

第二个功能:如果参数为String类型则将其视作选择器,返回当前元素在选择器所匹配的元素中的索引位置。如果该选择器不匹配任何元素或者当前元素不在匹配到的元素内,则返回-1。

if ( typeof elem === "string" ) {
 //在数组jQuery( elem )中搜索指定的值,并返回其索引值
 return jQuery.inArray( this[0], jQuery( elem ) );
}

第三个功能:如果object为DOM元素或jQuery对象,则返回该元素(或该jQuery对象中的第一个元素)在当前jQuery对象所匹配的元素中的索引位置。

return jQuery.inArray(elem.jquery ? elem[0] : elem, this ); 

其他的筛选处理就不分析了。看源码即可明白。

【相关教程推荐】

1. JavaScript视频教程
2. JavaScript在线手册
3. bootstrap教程

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn