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 相关文章推荐
再谈querySelector和querySelectorAll的区别与联系
Apr 20 Javascript
jquery实现每个数字上都带进度条的幻灯片
Feb 20 Javascript
js Date概念详细介绍
Nov 22 Javascript
解决html按钮切换绑定不同函数后点击时执行多次函数问题
May 14 Javascript
Jquery Ajax Error 调试错误的技巧
Nov 20 Javascript
JavaScript动态插入CSS的方法
Dec 10 Javascript
Knockoutjs 学习系列(一)ko初体验
Jun 07 Javascript
JS制作图形验证码实现代码
Oct 19 Javascript
jQuery常见的选择器及用法介绍
Dec 20 Javascript
Node.js复制文件的方法示例
Dec 29 Javascript
Vue+Mock.js模拟登录和表格的增删改查功能
Jul 26 Javascript
函数式编程入门实践(一)
Apr 20 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中try catch捕获异常实例详解
2014/11/21 PHP
javascript下4个跨浏览器必备的函数
2010/03/07 Javascript
图标线性回归斜着移动到指定的位置
2013/08/16 Javascript
javascript中clone对象详解
2014/12/03 Javascript
jQuery使用CSS()方法给指定元素同时设置多个样式
2015/03/26 Javascript
简易的投票系统以及js刷票思路和方法
2015/04/07 Javascript
php常见的页面跳转方法汇总
2015/04/15 Javascript
在JavaScript中操作时间之getYear()方法的使用教程
2015/06/11 Javascript
js流动式效果显示当前系统时间
2016/05/16 Javascript
jQuery处理XML文件的几种方法
2016/06/14 Javascript
bootstrap table 表格中增加下拉菜单末行出现滚动条的快速解决方法
2017/01/05 Javascript
详谈js中window.location.search的用法和作用
2017/02/13 Javascript
canvas仿iwatch时钟效果
2017/03/06 Javascript
Django使用多数据库的方法
2017/09/06 Javascript
JS+HTML+CSS实现轮播效果
2017/11/28 Javascript
vue-cli中安装方法(图文详细步骤)
2018/12/12 Javascript
Servlet返回的数据js解析2种方法
2019/12/12 Javascript
jquery绑定事件 bind和on的用法与区别分析
2020/05/22 jQuery
vue axios封装httpjs,接口公用配置拦截操作
2020/08/11 Javascript
vue实现前端列表多条件筛选
2020/10/26 Javascript
python的常见命令注入威胁
2013/02/18 Python
python使用nntp读取新闻组内容的方法
2015/05/08 Python
详解python多线程之间的同步(一)
2019/04/03 Python
Python进程间通信 multiProcessing Queue队列实现详解
2019/09/23 Python
大家都说好用的Python命令行库click的使用
2019/11/07 Python
Python 中@property的用法详解
2020/01/15 Python
基于Pycharm加载多个项目过程图解
2020/01/19 Python
利用python控制Autocad:pyautocad方式
2020/06/01 Python
CSS3实现内凹圆角的实例代码
2017/05/04 HTML / CSS
html5应用缓存_动力节点Java学院整理
2017/07/13 HTML / CSS
七一表彰活动方案
2014/01/18 职场文书
2015年八一建军节活动总结
2015/03/20 职场文书
酒店优秀员工推荐信
2015/03/24 职场文书
新郎结婚感言
2015/07/31 职场文书
解除租赁合同协议书
2016/03/21 职场文书
员工工作失职检讨书范文!
2019/07/03 职场文书