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


Posted in Javascript onDecember 02, 2015

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

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") 所有

元素

.class.class $(".intro.demo") 所有 class="intro" 且 class="demo" 的元素
     
:first $("p:first") 第一个

元素

:last $("p:last") 最后一个

元素

:even $("tr:even") 所有偶数
:odd $("tr:odd") 所有奇数
     
: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") 所有标题元素

-

:animated   所有动画元素
     
:contains(text) $(":contains('W3School')") 包含指定字符串的所有元素
:empty $(":empty") 无子(元素)节点的所有元素
:hidden $("p:hidden") 所有隐藏的

元素

: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") 所有 元素
:text $(":text") 所有 type="text" 的 元素
:password $(":password") 所有 type="password" 的 元素
:radio $(":radio") 所有 type="radio" 的 元素
:checkbox $(":checkbox") 所有 type="checkbox" 的 元素
:submit $(":submit") 所有 type="submit" 的 元素
:reset $(":reset") 所有 type="reset" 的 元素
:button $(":button") 所有 type="button" 的 元素
:image $(":image") 所有 type="image" 的 元素
:file $(":file") 所有 type="file" 的 元素
     
:enabled $(":enabled") 所有激活的 input 元素
:disabled $(":disabled") 所有禁用的 input 元素
:selected $(":selected") 所有被选取的 input 元素
:checked $(":checked") 所有被选中的 input 元素
Javascript 相关文章推荐
鼠标悬浮显示二级菜单效果的jquery实现
Oct 29 Javascript
详解AngularJS中的表格使用
Jun 16 Javascript
Javascript json object 与string 相互转换的简单实现
Sep 27 Javascript
canvas绘图不清晰的解决方案
Feb 28 Javascript
微信小程序实现根据字母选择城市功能
Aug 16 Javascript
vue router学习之动态路由和嵌套路由详解
Sep 21 Javascript
vue.js element-ui tree树形控件改iview的方法
Mar 29 Javascript
react配合antd组件实现的管理系统示例代码
Apr 24 Javascript
详解js类型判断
May 22 Javascript
JS拖动选择table里的单元格完整实例【基于jQuery】
May 28 jQuery
javascript Canvas动态粒子连线
Jan 01 Javascript
wepy--用vantUI 实现上弹列表并选择相应的值操作
Nov 03 Javascript
日常收藏的jquery技巧
Dec 02 #Javascript
JavaScript使用DeviceOne开发实战(三)仿微信应用
Dec 02 #Javascript
jquery密码强度校验
Dec 02 #Javascript
基于jQuery实现的双11天猫拆红包抽奖效果
Dec 01 #Javascript
jquery实现全选、反选、获得所有选中的checkbox
Sep 13 #Javascript
js实现继承的5种方式
Dec 01 #Javascript
6种javascript显示当前系统时间代码
Dec 01 #Javascript
You might like
PHP中文件上传的一个问题
2010/09/04 PHP
通达OA公共代码 php常用检测函数
2011/12/14 PHP
深入PHP magic quotes的详解
2013/06/17 PHP
PHP实现上传多文件示例代码
2017/02/20 PHP
浅谈javascript 面向对象编程
2009/10/28 Javascript
用JS实现一个TreeMenu效果分享
2011/08/28 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
jQuery如何获取动态添加的元素
2016/06/24 Javascript
JS正则替换去空格的方法
2017/03/24 Javascript
vue-router单页面路由
2017/06/17 Javascript
js技巧之十几行的代码实现vue.watch代码
2018/06/09 Javascript
解决IOS端微信H5页面软键盘弹起后页面下方留白的问题
2019/06/05 Javascript
小程序自定义弹框效果
2020/11/16 Javascript
Django框架下在视图中使用模版的方法
2015/07/16 Python
Python语言实现获取主机名根据端口杀死进程
2016/03/31 Python
Python中多线程的创建及基本调用方法
2016/07/08 Python
使用Django启动命令行及执行脚本的方法
2018/05/29 Python
Python任意字符串转16, 32, 64进制的方法
2019/06/12 Python
python 实现Flask中返回图片流给前端展示
2020/01/09 Python
h5封装下拉刷新
2020/08/25 HTML / CSS
方太官方网上商城:销售方太抽油烟机、燃气灶、消毒柜等
2017/01/17 全球购物
盛大笔试题
2016/11/05 面试题
企业治理工作自我评价
2013/09/26 职场文书
护理专业毕业生自我鉴定
2013/10/08 职场文书
大专计算机个人求职的自我评价
2013/10/21 职场文书
路政管理专业推荐信
2013/11/11 职场文书
村抢险救灾方案
2014/05/09 职场文书
项目申请汇报材料
2014/08/16 职场文书
2014年高数考试作弊检讨书
2014/12/14 职场文书
终止解除劳动合同证明书
2015/06/17 职场文书
2015小学音乐教师个人工作总结
2015/07/21 职场文书
2015年酒店销售部工作总结
2015/07/24 职场文书
公司财务管理制度
2015/08/04 职场文书
选对餐饮营销策略,营业额才会上涨
2019/08/27 职场文书
如何理解PHP核心特性命名空间
2021/05/28 PHP
如何正确理解python装饰器
2021/06/15 Python