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 相关文章推荐
javascript中callee与caller的用法和应用场景
Dec 08 Javascript
通过JS自动隐藏手机浏览器的地址栏实现原理与代码
Jan 02 Javascript
javascript 实现子父窗体互相传值的简单实例
Feb 17 Javascript
Node.js中的流(Stream)介绍
Mar 30 Javascript
js实现大转盘抽奖游戏实例
Jun 24 Javascript
使用postMesssage()实现跨域iframe页面间的信息传递方法
Mar 29 Javascript
jquery.multiselect多选下拉框实现代码
Nov 11 Javascript
js 点击a标签 获取a的自定义属性方法
Nov 21 Javascript
JavaScript 完成注册页面表单校验的实例
Aug 19 Javascript
vue2中的keep-alive使用总结及注意事项
Dec 21 Javascript
Vue-Cli 3.0 中配置高德地图的两种方式
Jun 19 Javascript
jquery实现轮播图特效
Apr 12 jQuery
日常收藏的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 Static关键字实用方法
2010/06/04 PHP
PHP中file_exists()判断中文文件名无效的解决方法
2014/11/12 PHP
ThinkPHP采用原生query实现关联查询left join实例
2014/12/02 PHP
比较全面的event对像在IE与FF中的区别 推荐
2009/09/21 Javascript
Webkit的跨域安全问题说明
2011/09/13 Javascript
前后台交互过程中json格式如何解析以及如何生成
2012/12/26 Javascript
JQuery插件fancybox无法在弹出层使用左右键的解决办法
2013/12/25 Javascript
js实现简单的左右两边固定广告效果实例
2015/04/10 Javascript
举例详解JavaScript中Promise的使用
2015/06/24 Javascript
JavaScript tab选项卡插件实例代码
2016/02/23 Javascript
javascript实现粘贴qq截图功能(clipboardData)
2016/05/29 Javascript
jQuery表单验证简单示例
2016/10/17 Javascript
微信小程序 textarea 详解及简单使用方法
2016/12/05 Javascript
three.js中文文档学习之通过模块导入
2017/11/20 Javascript
vue.extend实现alert模态框弹窗组件
2018/04/28 Javascript
Spring boot 和Vue开发中CORS跨域问题解决
2018/09/05 Javascript
JS添加或删除HTML dom元素的方法实例分析
2019/03/05 Javascript
[01:00:17]DOTA2-DPC中国联赛 正赛 SAG vs Dynasty BO3 第二场 1月25日
2021/03/11 DOTA
跟老齐学Python之做一个小游戏
2014/09/28 Python
浅谈python中set使用
2016/06/30 Python
Python实现注册登录系统
2017/08/08 Python
python使用Matplotlib画条形图
2020/03/25 Python
Python3 SSH远程连接服务器的方法示例
2018/12/29 Python
详解django+django-celery+celery的整合实战
2019/03/19 Python
spyder 在控制台(console)执行python文件,debug python程序方式
2020/04/20 Python
Selenium 配置启动项参数的方法
2020/12/04 Python
python中slice参数过长的处理方法及实例
2020/12/15 Python
瑞典Happy Socks美国官网:购买色彩斑斓的快乐袜子
2016/10/19 全球购物
英国游戏机和游戏购物网站:365games.co.uk
2018/06/18 全球购物
美国领先的低折扣旅行网站:Hotwire
2019/01/19 全球购物
不打扫卫生检讨书
2014/02/12 职场文书
态度决定一切演讲稿
2014/05/20 职场文书
学习十八大宣传标语
2014/10/09 职场文书
企业介绍信范文
2015/01/30 职场文书
2015年度班主任自我评价
2015/03/11 职场文书
社区党支部公开承诺书
2015/04/29 职场文书