jQuery源码分析系列:DOM遍历方法

jQuery DOM遍历:

    childNodes:包含HTML元素 文本。
    children:  只包含HTML元素。

    jQuery.fn.children(elem):只包含Elemnet
    jQuery.fn.contents(elem):只含Element Text Comment

 

实现jQuery遍历的具体方法:

 1 jQuery.each({
 2         parent:function(elem){//父元素
 3             var parent = elem.parentNode;
 4             return parent && parent.nodeType !== 11 ? parent : null;//DocumentFragment 11,DocumentType 10
 5         },
 6         parents:function(elem){
 7             return jQuery.dir(elem,"parentNode");//检索所有父元素,直至document
 8         },
 9         parentsUntil:function(elem,i,until){
10             return jQuery.dir(elem,"parentNode",until);
11         },
12         next:function(elem){//下一个兄弟元素
13             return jQuery.nth(elem,2,"nextSibling");//从1开始计数,当前为1
14         },
15         prev:function(elem){//上一个兄弟元素
16             return jQuery.nth(elem,2,"previousSibling");
17         },
18         nextAll:function(elem){//所有的兄弟元素
19             return jQuery.dir(elem,"nextSibling");
20         },
21         prveAll:function(elem){//所有兄长元素
22             return jQuery.dir(elem,"previousSibling");
23         },
24         nextUnitl:function(elem,i,until){//所有的兄弟元素,但不包括until
25             return jQuery.dir(elem,"nextSibling",until);
26         },
27         prevUntil:Function(elem,i,until){//所有的兄长元素,但不包括until
28             return jQuery.dir(elem,"previousSibling",until);
29         },
30 
31         siblings:function(elem){//所有的兄长,兄弟元素 不包括当前元素
32             // 父元素的第一个子元素的所有兄弟元素,排除当前元素elem
33             // 取子元素childNodes然后过滤elem,效率更高
34             return jQuery.sibling(elem.parentNode.firstChild,elem);
35         },
36         children: function( elem ) { // 所有的子节点,只包含Element
37             return jQuery.sibling( elem.firstChild ); // 第一个子元素的所有兄弟元素
38             // 为什么不直接取子元素呢?childNodes不是效率更高么?
39         },
40         contents: function( elem ) { // 所有的子节点,包含Element、Text、Comment
41             return jQuery.nodeName( elem, "iframe" ) ?
42                 elem.contentDocument || elem.contentWindow.document : // 如果是iframe,则取document
43                 jQuery.makeArray( elem.childNodes ); // 将childNodes转换数组,childNodes是伪数组,转换后遍历数组时可以避免对childNodes进行检查,提高性能
44         }
45     },function(name,fn){
46         jQuery.fn[name] = function(until,selector){
47             var ret = jQuery.map(this,fn,until),//将this中的元素,用fn处理最后返回真正的数组  until用于过滤
48                 args = slice.call(arguments);//将arguments赋值并转为数组
49             if(!runtil.test(name)){//不以until结尾
50                 selector = until;//不需要参数until 只有一个参数selector util知道这里为止
51             }
52             if(selector && typeof selector === "string"){
53                 ret = jQueyr.filter(selector,ret);//对ret数组用selector进行过滤 只留下匹配的元素
54                 // jQuery.filter会调用jQuery.find.matches > Sizzle.matches > Sizzle,Sizzle查找、过滤的结果已经经过排序、去重
55             }
56             /**
57              * 排序、去重
58              * 首先要知道this是经过排序、且无重复
59              * 如果长度大于1,才需要判断是否需要去重
60              * 如果name是 children contents next prev 之一,则不需要排序去重,因为这四个方法不会产生无序和重复的结果,为什么呢?
61              * 因为this中的元素是有序、去重的,所以的它子节点、兄弟、兄长,也都是有序、无重复的
62              * 但是parent parents parentsUntil nextAll prevAll nextUntil prevUntil siblings却有可能产生重复、无序的元素
63              */
64             ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; // 去重
65 
66             // rparentsprev = /^(?:parents|prevUntil|prevAll)/
67             // 因为前边返回的结果是按照元素在文档中的位置顺序返回的,遇到rparentsprev则需要再反过来,方便使用
68             if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
69                 ret = ret.reverse(); // 倒序
70             }
71 
72             return this.pushStack( ret, name, args.join(",") ); // 构造jQuery对象
73         }
74 });

 

DOM遍历的三个核心方法:


            DOM遍历的三个核心函数:
            jQuery.dir(elem,dir,until)         :从一个元素触发,迭代检索某个方向上的所有元素并记录,知道与遇到docuemnt对象或遇到until匹配的元素
            jQuery.nth(cur,result,dir,elem)    :从一个元素触发,迭代检索某个方向上的第N个元素
            jQuery.sibling(n,elem)            :元素n的所有后续兄弟元素,包含n,不包含elem

 1 jQuery.extend({
 2         /*
 3             DOM遍历的三个核心函数:
 4             jQuery.dir(elem,dir,until)         :从一个元素触发,迭代检索某个方向上的所有元素并记录,知道与遇到docuemnt对象或遇到until匹配的元素
 5             jQuery.nth(cur,result,dir,elem)    :从一个元素触发,迭代检索某个方向上的第N个元素
 6             jQuery.sibling(n,elem)            :元素n的所有后续兄弟元素,包含n,不包含elem
 7 
 8         */
 9         /*
10             迭代条件:cur.nodeType !== 9 && !jQuery(cur).is(until)
11             elem:    起始元素
12             dir:    迭代方向,可选值:parentNode  nextSibling previousSibling
13             until     选择器表达式 如果遇到until匹配的元素 迭代终止
14         */
15         dir:function(elem,dir,until){//支持遍历祖先 所有兄长 所有兄弟
16             var matched = [],//匹配结果
17                 cur = elem[dir];//第一次访问  在dir方向上取一次 为循环提供第一次判断
18                 //不包含自身
19                 /*
20                     迭代访问 知道遇到document对象或遇到until匹配的元素
21                     cur.nodeType !== 9  当前DOM节点cur不是document对象  nodeType = 1 是element
22                     !jQuery(cur).is(until) 当前DOM节点cur不匹配表达式until
23 
24                     这里或运算的过程中  满足这个条件until !==undefined && cur.nodeType ===1才会调用jQuery.is
25                 */
26             while(cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery(cur).is(until))){
27                 if(cur.nodeType === 1){//如果是Element元素 则放入匹配结果数组
28                     matched.push(cur);//符合要求 放入数组
29                 }
30                 cur = cur[dir];//将方向dir上的下一个节点 设置为当前节点 继续访问
31             }
32             return matched;
33         },
34         /*
35             从一个元素触发,迭代检索某个方向上的第N个元素
36 
37             cur 起始元素
38             result第几个 1表示当前元素 2表示下一个
39             dir 迭代方向
40             elem 多余
41         */
42         nth:function(cur,result,dir,elem){
43             result = result || 1;//默认为从1开始
44             var num =0;
45 
46             for(;cur;cur = cur[dir]){//先判断再取,结果中包含n
47                 //检索到了这个元素
48                 if(cur.nodeType === 1 && ++ num === result){
49                     break;
50                 }
51             }
52             //如果result为0 或者1 取当前元素
53             //如果result小于0  则放回undefined
54             return cur;
55         },
56         /*
57             元素n的所有后续兄弟元素 包含n  不包含elem
58             n 起始元素
59             elem排除元素
60         */
61         sibling:function(n,elem){
62             var r = [];
63             for(;n;n = n.nextSibling){//先判断再取,结果中包含n
64             //过滤掉其他的Text Attr Comment等元素
65                 if(n.nodeType === 1 && n !== elem){
66                     r.push(n);
67                 }
68             }
69             return r;
70         }
71 });

 

 

 

原文地址:https://www.cnblogs.com/colorstory/p/2621675.html