Jquery 1.9.1源码分析系列(十二)之筛选操作

废话不多说了直接奔入主题了。

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

  可以看出,这几个筛选步骤一致。都是先通过map函数把当前jQuery对象每个匹配的元素代入相应的匹配函数(fn)中获取出结果然后在进行后续的过滤。

  我们先看一下后面的过滤(已经通过jQuery.map( this, fn, until )获取到了备选种子ret)
  首先,并不是所有的筛选函数都有until这个参数,只有以Until结尾的几个筛选才需要这个参数,其他的筛选只有selector这个参数。

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

  其次,如果有选择器,则通过选择器过滤一下先前查找结果ret

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

  然后,guaranteedUnique里面的几种筛选条件(children/contents/next/prev)在当前jQuery对象所匹配的元素个数有多个的时候,通过每个匹配元素获取到的结果保存在结果集ret中,且不需要去重。其他筛选是要去重的。点击查看jQuery.unique方法详解

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

  另外,还需要处理的特殊情况是: 如果当前jQuery对象所匹配的元素有多个,则使用parents /prevUntil /prevAll这三种筛选的结果需要倒序排列。需要倒序的原因:jQuery.unique使用的是Sizzle引擎中的排序函数Sizzle .uniqueSort,这个排序函数会根据文档最顶层对象到最底层的方式排列。

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

  最后,返回包裹后的结果

return this.pushStack( ret ); 

  上面说了主题的框架结构,下面说一下这一组筛选器匹配函数里面用到的两个函数jQuery.dir和jQuery. sibling,直接上源码
//从当前元素elem指定的dir对应的节点开始一直查找dir,并将这些节点保存在matched中,直到循环终止。注意:结果中不包含elem节点

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.fn.add( selector, context )和jQuery.fn. addBack( selector )

  add函数是向当前匹配元素中添加符合指定表达式的元素,并以jQuery对象的形式返回。add可以接收包括:选择器(字符串)、HTML内容(字符串)、DOM元素(Element)、jQuery对象。处理比较简单,直接上源码

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

  not和filter都是操作本身的集合,not是过滤掉本身集合中满足过滤条件selector的项,留下其他项。而filter是留下满足过滤条件selector的项。

  关键是function winnow( elements, qualifier, keep )函数。这个函数的功能是执行相同的过滤或者不过滤的功能。过滤条件qualifier有三种:函数、DOM节点、字符串。keep:true表示保留满足过滤条件的项,false表示保留不满足过滤条件的项。

winnow的源码注释如下

//执行相同的过滤或者不过滤的功能
function winnow( elements, qualifier, keep ) {
 // Can'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.grep,grep详解点击这里。

  jQuery.filter( expr, elems, not )这个低级api专门用来处理jQuery.fn.filter中过滤条件为字符串的情况。

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 )

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

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

jquery选择器

选择器 实例 选取
* $("*") 所有元素
#id $("#lastname") id="lastname" 的元素
.class $(".intro") 所有 class="intro" 的元素
element $("p") 所有 <p> 元素
.class.class $(".intro.demo") 所有 class="intro" 且 class="demo" 的元素
     
:first $("p:first") 第一个 <p> 元素
:last $("p:last") 最后一个 <p> 元素
:even $("tr:even") 所有偶数 <tr> 元素
:odd $("tr:odd") 所有奇数 <tr> 元素
     
:eq(index) $("ul li:eq(3)") 列表中的第四个元素(index 从 0 开始)
:gt(no) $("ul li:gt(3)") 列出 index 大于 3 的元素
:lt(no) $("ul li:lt(3)") 列出 index 小于 3 的元素
:not(selector) $("input:not(:empty)") 所有不为空的 input 元素
     
:header $(":header") 所有标题元素 <h1> - <h6>
:animated   所有动画元素
     
:contains(text) $(":contains('W3School')") 包含指定字符串的所有元素
:empty $(":empty") 无子(元素)节点的所有元素
:hidden $("p:hidden") 所有隐藏的 <p> 元素
:visible $("table:visible") 所有可见的表格
     
s1,s2,s3 $("th,td,.intro") 所有带有匹配选择的元素
     
[attribute] $("[href]") 所有带有 href 属性的元素
[attribute=value] $("[href='#']") 所有 href 属性的值等于 "#" 的元素
[attribute!=value] $("[href!='#']") 所有 href 属性的值不等于 "#" 的元素
[attribute$=value] $("[href$='.jpg']") 所有 href 属性的值包含以 ".jpg" 结尾的元素
     
:input $(":input") 所有 <input> 元素
:text $(":text") 所有 type="text" 的 <input> 元素
:password $(":password") 所有 type="password" 的 <input> 元素
:radio $(":radio") 所有 type="radio" 的 <input> 元素
:checkbox $(":checkbox") 所有 type="checkbox" 的 <input> 元素
:submit $(":submit") 所有 type="submit" 的 <input> 元素
:reset $(":reset") 所有 type="reset" 的 <input> 元素
:button $(":button") 所有 type="button" 的 <input> 元素
:image $(":image") 所有 type="image" 的 <input> 元素
:file $(":file") 所有 type="file" 的 <input> 元素
     
:enabled $(":enabled") 所有激活的 input 元素
:disabled $(":disabled") 所有禁用的 input 元素
:selected $(":selected") 所有被选取的 input 元素
:checked $(":checked") 所有被选中的 input 元素
(0)

相关推荐

  • jQuery 1.9.1源码分析系列(十)事件系统之绑定事件

    事件绑定的方法有很多种,使用了jquery那么原理那种绑定方式(elem.click = function(){...}))就不太想推荐给大家了.最主要的原因是elem.click=fn这种方式只能绑定一个事件处理,多次绑定的只会保留最后一次绑定的结果. 下面给大家介绍jquery绑定事件的方式有哪些吧. 复制代码 代码如下: jQuery.fn.eventType([[data,] fn]) 比如eventType指的是事件类型,比如click: $("#chua").click(f

  • jQuery 1.9.1源码分析系列(十四)之常用jQuery工具

    为了给下一章分析动画处理做准备,先来看一下一些工具.其中队列工具在动画处理中被经常使用. jQuery.fn. queue(([ queueName ] [, newQueue ]) || ([ queueName ,] callback ))(获取或设置当前匹配元素上待执行的函数队列. 如果当前jQuery对象匹配多个元素:获取队列时,只获取第一个匹配元素上的队列:设置队列(替换队列.追加函数)时,则为每个匹配元素都分别进行设置.如果需要移除并执行队列中的第一个函数,请使用dequeue()函

  • jQuery-1.9.1源码分析系列(十)事件系统之事件体系结构

    又是一个重磅功能点. 在分析源码之前分析一下体系结构,有助于源码理解.实际上在jQuery出现之前,Dean Edwards的跨浏览器AddEvent()设计做的已经比较优秀了:而且jQuery事件系统的设计思想也是基于该思想的,所以我们先分析一下Dean Edwards前辈的事件绑定. a. jQuery事件原型--Dean Edwards的跨浏览器AddEvent()设计 源码解读 //事件添加方法 function addEvent(element, type, handler) { //

  • Jquery-1.9.1源码分析系列(十一)之DOM操作

    DOM操作包括append.prepend.before.after.replaceWith.appendTo.prependTo.insertBefore.insertAfter.replaceAll.其核心处理函数是domManip. DOM操作函数中后五种方法使用的依然是前面五种方法,源码 jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before&quo

  • jQuery 1.9.1源码分析系列(十三)之位置大小操作

    先给大家展示谢 jQuery.fn.css (propertyName [, value ]| object )(函数用于设置或返回当前jQuery对象所匹配的元素的css样式属性值.如果需要删除指定的css属性,请使用该函数将其值设为空字符串("") 注意:1.如果省略了value参数,则表示获取属性值:如果指定了该参数,则表示设置属性值.2.css()函数的所有"设置"操作针对的是当前jQuery对象所匹配的每一个元素:所有"读取"操作只针对

  • jQuery1.9.1针对checkbox的调整方法(prop)

    在jquery 1.8.x中的版本,我们对于checkbox的选中与不选中操作如下: 判断是否选中 复制代码 代码如下: $('#checkbox').prop('checked') 设置选中与不选中状态: 复制代码 代码如下: $('#checkbox').attr('checked',true)$('#checkbox').attr('checked',false) 但此方法在jquery1.9.1中,有些处理不一样 IE10,Chrome,FF中,对于选中状态,第一次$('#checkbo

  • jQuery-1.9.1源码分析系列(十)事件系统之事件包装

    在上篇文章给大家介绍了jQuery-1.9.1源码分析系列(十)事件系统之事件体系结构,本篇继续给大家介绍jquery1.9.1源码分析系列相关知识,具体内容请看下文吧. 首先需要明白,浏览器的原生事件是只读的,限制了jQuery对他的操作.举个简单的例子就能明白为什么jQuery非要构造一个新的事件对象. 在委托处理中,a节点委托b节点在a被click的时候执行fn函数.当事件冒泡到b节点,执行fn的时候上下文环境需要保证正确,是a节点执行了fn而非b节点.如何保证执行fn的上下文环境是a节点

  • jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween

    在jQuery内部函数Animation中调用到了createTweens()来创建缓动动画组,创建完成后的结果为: 可以看到上面的缓动动画组有四个原子动画组成.每一个原子动画的信息都包含在里面了. 仔细查看createTweens函数,实际上就是遍历调用了tweeners ["*"]的数组中的函数(实际上就只有一个元素). function createTweens( animation, props ) { jQuery.each( props, function( prop, v

  • jQuery-1.9.1源码分析系列(十一)DOM操作续之克隆节点

    什么情况下使用到克隆节点? 我们知道在对DOM操作过程中如果直接使用节点会出现节点随操作而变动的情况.比如对节点使用.after/.before/.append等方法后,节点被添加到新的地方,原来的位置上的节点被移除了.有的时候需要保留原来位置上的节点,仅仅是需要一个副本添加到对应位置,这个时候克隆就有了使用场景. jQuery.fn.clone克隆当前匹配元素集合的一个副本,并以jQuery对象的形式返回. 你还可以指定是否复制这些匹配元素(甚至它们的子元素)的附加数据( data()函数 )

  • Jquery1.9.1源码分析系列(六)延时对象应用之jQuery.ready

    还记不记得jQuery初始化函数jQuery.fn.init中有这样是一个分支 //document ready简便写法$(function(){-}) } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } 所以$(fn)===$(document).ready(fn). 来看一下jQuery.fn.ready的源码 ready: function( fn ) { // Add t

  • Android Tween动画之RotateAnimation实现图片不停旋转效果实例介绍

    主要介绍Android中如何使用rotate实现图片不停旋转的效果.Android 平台提供了两类动画,一类是 Tween 动画,即通过对场景里的对象不断做图像变换(平移.缩放.旋转)产生动画效果:第二类是 Frame 动画,即顺序播放事先做好的图像,跟电影类似.本文分析 Tween动画的rotate实现旋转效果. 在新浪微博客户端中各个操作进行中时activity的右上角都会有个不停旋转的图标,类似刷新的效果,给用户以操作中的提示.这种非模态的提示方式推荐使用,那么下面就分享下如何实现这种效果

  • jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理

    发现一个小点,先前没有注意的 stopPropagation: function() { var e = this.originalEvent; ... if ( e.stopPropagation ) { e.stopPropagation(); } jQuery重载stopPropagation函数调用的本地事件对象的stopPropagation函数阻止冒泡.也就是说,阻止冒泡的是当前节点,而不是事件源. 说到触发事件,我们第一反应是使用$(...).click()这种方式触发click事

随机推荐