jQuery 源码分析(十九) DOM遍历模块详解

jQuery的DOM遍历模块对DOM模型的原生属性parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling进行了封装和扩展,用于在DOM树中遍历父元素、子元素和兄弟元素。

可以通过jQuery的实例来访问,方法如下:

  • parent()                  ;获取匹配元素的父元素
  • parents(selector)           ;获取匹配元素的所有祖先元素                                    ;selector用于过滤查找的元素,如果为空则获取所有的祖先元素
  • parentsUntil(until,selector)    ;获得当前匹配元素集合中每个元素的祖先元素,直到遇到until元素
  • next()                     ;获取匹配元素之后紧挨着的兄弟元素,没有则返回undefined
  • nextAll(selector)               ;获取匹配元素之后紧挨着的所有兄弟元素
  • nextUntil(until,selector)       ;获取匹配元素之后紧挨着的所有兄弟元素,直到遇到until元素
  • prev()                    ;获取匹配元素之前紧挨着的兄弟元素,没有则返回undefined
  • prevAll(selector)              ;获取匹配元素之前紧挨着的所有兄弟元素
  • prevUntil(until,selector)        ;获取匹配元素之前紧挨着的所有兄弟元素,直到遇到until元素
  • siblings(selector)            ;获取匹配元素的所有兄弟元素,如果指定了selector则只获取该类型的元素
  • children(selector)           ;获取匹配元素的子元素,不包括文本节点和注释节点
  • contents()                  ;获取匹配元素的子元素,包括文本节点和注释节点

举个栗子:

writer by:大沙漠 QQ:22969969

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <h1>Hello World!</h1>
        <p>123</p>
        <span>123</span>
    </div>
    <script>
        console.log( $(‘p‘).parent() )            //获取p元素的父元素
        console.log( $(‘p‘).prev() )              //获取p元素之前紧挨的兄弟元素
        console.log( $(‘p‘).next() )              //获取p元素之后紧挨的兄弟元素
        console.log( $(‘p‘).siblings() )          //获取p元素的所有兄弟元素
        console.log( $(‘div‘).children() )        //获取div的所有子元素,不包括文本节点和注释节点
        console.log( $(‘div‘).contents() )        //获取div的所有子元素,包括文本节点和注释节点
    </script>
</body>
</html>

输出如下:

对应栗子里的每一个输出,分别输出

  • p元素的父节点
  • p元素的上一个兄弟元素
  • p元素的下一个兄弟元素
  • p元素的所有兄弟元素
  • div元素过滤掉文本节点和空白节点的子节点
  • div元素包括文本节点和空白节点的子节点

返回的都是一个jQuery对象,非常的好用,操作DOM时都会用到这几个方法

源码分析



jQuery对于DOM遍历模块的实现是基于三个工具函数实现的

  • $.dir(elem, dir, until)       ;从elem元素开始,查找dir方向(可以是:parentNode、nextSibling或previousSibling)上的所有元素,如果在查找过程中遇到匹配参数until的元素,则查找终止
  • $.nth(cur, result, dir, elem)  ;从cur元素出发,查找dir方向(parentNode、nextSibling或previousSibling)上的第result个元素
  • $.sibling(n, elem)       ;负责查找一个元素之后的所有兄弟元素,包括起始元素,但不包括参数elem。n是查找的起始元素,包含在结果集中,elem是可选的DOM元素,不包含在返回结果中。

举个栗子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <h1>Hello World!</h1>
        <p>123</p>
        <span>123</span>
    </div>
    <script>
        var h1 = document.getElementsByTagName(‘h1‘)[0],          //分别获取h1、p、span元素的节点引用
            p = document.getElementsByTagName(‘p‘)[0],
            span = document.getElementsByTagName(‘span‘)[0];
        console.log( $.dir(h1,‘parentNode‘) )                     //打印h1的所有祖先节点
        console.log( $.nth(h1,3,‘nextSibling‘) )                  //获取h1之后的第二个节点
        console.log( $.sibling(h1,h1) )                           //获取h1的所有兄弟元素,默认获取包括自身在内的元素,参数2也传入h1表示排除h1元素
    </script>
</body>
</html>

输出如下:

输出三行,分别对应h1的所有祖先节点、h1之后的第二个子节点和h1的所有兄弟元素,和DOM树中也是对应的。

对于$.dir、$.nth和$.sibling来说,它的实现如下:

jQuery.extend({
    dir: function( elem, dir, until ) {        //从elem元素开始,查找dir方向(可以是:parentNode、nextSibling或previousSibling)上的所有元素,如果在查找过程中遇到匹配参数until的元素,则查找终止。
        var matched = [],
            cur = elem[ dir ];                    //cur表示下一个元素,根据参数dir不同,可能是父元素、前一个兄弟元素 或者 后一个兄弟元素

        while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {    //如果下一个元素存在 且 不是document对象(nodeType等于9) 则继续判断括号里的语句,否则跳出循环  括号内:如果没有传入until参数,或者传入了until参数并且当前元素不是Element节点(属性nodeType等于1)    或者 传入了until参数且当前参数是Element节点且该元素不匹配参数until 则进行循环
            if ( cur.nodeType === 1 ) {                    //如果cur是元素节点
                matched.push( cur );                        //存储到数组matched中
            }
            cur = cur[dir];                                //获取下一个方向上的元素
        }
        return matched;                                //返回找到了的Element节点数组。
    },

    nth: function( cur, result, dir, elem ) {    //从cur元素出发,查找dir方向(parentNode、nextSibling或previousSibling)上的第result个元素
        result = result || 1;
        var num = 0;                                //当前查找的元素序号

        for ( ; cur; cur = cur[dir] ) {                    //从起始元素cur触发,沿着方向dir迭代遍历,只要cur元素存在则一直查找,直到查找该方向上的第result个Element节点
            if ( cur.nodeType === 1 && ++num === result ) {    //如果cur是一个元素节点,则num+1,此时如果num等于result,则表示查找到该元素了,则跳出循环
                break;
            }
        }

        return cur;                                        //返回查找到的cur元素。
    },

    sibling: function( n, elem ) {                //负责查找一个元素之后的所有兄弟元素,包括起始元素,但不包括参数elem。n是查找的起始元素,包含在结果集中,elem是可选的DOM元素,不包含在返回结果中。
        var r = [];

        for ( ; n; n = n.nextSibling ) {                //从起始元素n触发,迭代遍历其后的所有兄弟元素
            if ( n.nodeType === 1 && n !== elem ) {            //如果该兄弟元素是元素节点且不等于参数elem则被放入数组r中。
                r.push( n );
            }
        }

        return r;                                        //返回存放了找到的Element节点的数组r
    }
});

也就是对parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling这些原生DOM操作的一些封装

我们通过jQuery实例可以访问的方法的实现如下:

jQuery.each({
    parent: function( elem ) {                        //返回匹配元素的父元素
        var parent = elem.parentNode;
        return parent && parent.nodeType !== 11 ? parent : null;    //如果父元素存在且不是文档碎片元素,则返回该父元素,否则返回null
    },
    parents: function( elem ) {                        //返回匹配元素的所有祖先元素
        return jQuery.dir( elem, "parentNode" );
    },
    parentsUntil: function( elem, i, until ) {        //获得当前匹配元素集合中每个元素的祖先元素,直到遇到until元素
        return jQuery.dir( elem, "parentNode", until );
    },
    next: function( elem ) {                        //获取匹配元素之后紧挨着的兄弟元素
        return jQuery.nth( elem, 2, "nextSibling" );
    },
    prev: function( elem ) {                        //获取匹配元素之前紧挨着的兄弟元素
        return jQuery.nth( elem, 2, "previousSibling" );
    },
    nextAll: function( elem ) {                        //获取匹配元素之后紧挨着的所有兄弟元素
        return jQuery.dir( elem, "nextSibling" );
    },
    prevAll: function( elem ) {                        //获取匹配元素之前紧挨着的所有兄弟元素
        return jQuery.dir( elem, "previousSibling" );
    },
    nextUntil: function( elem, i, until ) {            //获取匹配元素之后紧挨着的所有兄弟元素    ,直到遇到匹配元素until为止
        return jQuery.dir( elem, "nextSibling", until );
    },
    prevUntil: function( elem, i, until ) {            //获取匹配元素之前紧挨着的所有兄弟元素    ,直到遇到匹配元素until为止
        return jQuery.dir( elem, "previousSibling", until );
    },
    siblings: function( elem ) {                    //获取匹配元素的所有兄弟元素
        return jQuery.sibling( elem.parentNode.firstChild, elem );
    },
    children: function( elem ) {                    //获取匹配元素的子元素
        return jQuery.sibling( elem.firstChild );
    },
    contents: function( elem ) {                    //获取匹配元素的子元素,包括文本节点和注释节点
        return jQuery.nodeName( elem, "iframe" ) ?
            elem.contentDocument || elem.contentWindow.document :
            jQuery.makeArray( elem.childNodes );
    }
}, function( name, fn ) {                            //模板函数,用于调用对应的遍历函数查找DOM元素,然后执行过滤、排序、和去重操作         name是每个函数名,fn是对应的值(也就是函数)
    jQuery.fn[ name ] = function( until, selector ) {        //定义模版函数,真正是在这里定义的,它接收两个参数 until:选择器表达式,用于指示查找停止的位置。selector:选择器表达式,用于过滤找到的元素
        var ret = jQuery.map( this, fn, until );                //调用方法jQuery.map()遍历当前匹配元素集合,对每个元素调用遍历函数fn,并将遍历函数fn的返回值放入一个新的数组ret中。

        if ( !runtil.test( name ) ) {                        //如果遍历函数名不以‘Until‘结尾的,则最多只有一个参数
            selector = until;                                    //修正参数selector为until
        }

        if ( selector && typeof selector === "string" ) {    //如果设置了参数selector 且selector是一个字符串
            ret = jQuery.filter( selector, ret );                //则调用jQuery.filter()对数组ret中的DOM元素逐个过滤,最终只保留匹配选择器表达式selector的元素。
        }

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

        if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {        //排序
            ret = ret.reverse();
        }

        return this.pushStack( ret, name, slice.call( arguments ).join(",") );    //创建一个jQuery对象并返回
    };
});

实现就是通过$.each()遍历一个对象,依次执行参数2这个函数,该函数会在$.fn上依次挂载每个接口,使用$.map()工具方法它会依次执行fn函数,进行一些过滤去重后最后调用$.pushStack()将数组转换为一个jQuery对象,这里理解起来有点难度,需要多理一下代码。

原文地址:https://www.cnblogs.com/greatdesert/p/11724756.html

时间: 2024-10-13 10:08:55

jQuery 源码分析(十九) DOM遍历模块详解的相关文章

jQuery 源码分析(十四) 数据操作模块 类样式操作 详解

jQuery的属性操作模块总共有4个部分,本篇说一下第3个部分:类样式操作部分,用于修改DOM元素的class特性的,对于类样式操作来说,jQuery并没有定义静态方法,而只定义了实例方法,如下: addClass(value) ;为匹配元素集合中的每个元素添加一个或多个类样式,通过修改DOM属性className来修改类样式,value可以是个以空格分隔的类样式或者一个函数(返回一个或多个以空格分隔的类样式) hasClass(selector)   ;检测匹配元素中的任意元素是否含有指定的类

jQuery 源码分析(十五) 数据操作模块 val详解

jQuery的属性操作模块总共有4个部分,本篇说一下最后一个部分:val值的操作,也是属性操作里最简单的吧,只有一个API,如下: val(vlaue)        ;获取匹配元素集合中第一个元素的当前值,或者设置匹配元素集合中每个元素的值,有三种用法: val()        ;没有参数,获取第一个匹配元素的当前值 val(value)      ;为每个匹配元素设置value值        ;如果为null则表示设置值为空 val(func)       ;设置每个匹配元素为函数fun

jQuery 源码分析(十八) ready事件详解

ready事件是当DOM文档树加载完成后执行一个函数(不包含图片,css等),因此它的触发要早于load事件.用法: $(document).ready(fun) ;fun是一个函数,这样当DOM树加载完毕后就会执行该匿名函数了 ready有一个简写,可以直接传入$(fun)即可,这是因为在jQuey内部也定义了一个$(document)的jQuery对象,和我们在上面的写法是一样的 ready事件和window的onload区别: ready事件 ;等dom树载完毕后就可以执行 onload事

Vue 2.0 深入源码分析(六) 基础篇 computed 属性详解

用法 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护,比如: <div id="example">{{ message.split('').reverse().join('') }}</div> <script> var app = new Vue({ el:'#example', data:{message:'hello world'} }) </script> 这样模板不再是简

JDK源码分析(12)之 ConcurrentHashMap 详解

本文将主要讲述 JDK1.8 版本 的 ConcurrentHashMap,其内部结构和很多的哈希优化算法,都是和 JDK1.8 版本的 HashMap是一样的,所以在阅读本文之前,一定要先了解 HashMap,可以参考 HashMap 相关:另外 ConcurrentHashMap 中同样有红黑树,这部分可以先不看不影响整体结构把握,有兴趣的可以查看 红黑树: 一.ConcurrentHashMap 结构概述 1. 整体概述 CHM 的源码有 6k 多行,包含的内容多,精巧,不容易理解:建议在

Vue 2.0 深入源码分析(五) 基础篇 methods属性详解

用法 methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="https://cdn.bootcss.com/vue/2.5.16/vue.js"></script> <title>Document&

ABP源码分析十九:Auditing

审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,它们提供了活动序列的文档证据,这些活动序列可以在任何时间影响一个特定的操作. AuditInfo:定义如下图中需要被Audit的信息. AuditedAttribute: 用于标识一个方法或一个类的所有方法都需要启用Auditing功能. DisableAuditingAttribute:用于标识一个方法或一个类的所有方法都需要关闭Auditing功能. IMvcControllersAuditingConfiguration/MvcCo

[转]jQuery源码分析系列

文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaron/jQuery 正在编写的书 - jQuery架构设计与实现 本人在慕课网的教程(完结) jQuery源码解析(架构与依赖模块) 64课时 jQuery源码解析(DOM与核心模块)64课时 jQuery源码分析目录(完结) jQuery源码分析系列(01) : 整体架构 jQuery源码分析系列(

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展