1. DOM遍历有3个核心函数:

jQuery.dir( elem, dir, until ) 从一个元素出发,迭代检索某个方向上的所有元素并记录,直到与遇到document对象或遇到until匹配的元素
jQuery.nth( cur, result, dir, elem ) 从一个元素出发,迭代检索某个方向上的第N个元素
jQuery.sibling( n, elem ) 元素n的所有后续兄弟元素,包含n,不包含elem

 

jQuery对象上的DOM遍历方法(大部分)被转换对这3个核心函数的调用,而这3个核心函数最后又都转换为对原生API的操作,看看他们之间的关系:

核心函数 jQuery对象方法 含义 用到的原生API
jQuery.dir parents 祖先元素 parentNode
  parentsUntil 祖先元素,但是不包括until parentNode
  nextAll 所有的兄弟元素 nextSibling
  prevAll 所有的兄长元素 previousSibling
  nextUntil 所有的兄弟元素,但是不包括until nextSibling
  prevUntil 所有的兄长元素,但是不包括until previousSibling
jQuery.nth next 下一个兄弟元素 nextSibling
  prev 上一个兄长元素 previousSibling
jQuery.sibling siblings 所有兄长、兄弟元素,不包括当前元素 elem.parentNode.firstChild
  children 所有的子节点 elem.firstChild

 

2. jQuery.dir( elem, dir, until )

/**
 * 从一个元素出发,迭代检索某个方向上的所有元素并记录,直到与遇到document对象或遇到until匹配的元素
 * 迭代条件(简化):cur.nodeType !== 9 && !jQuery( cur ).is( until )
 * elem	起始元素
 * dir	迭代方向,可选值:parentNode nextSibling previousSibling
 * until	选择器表达式,如果遇到until匹配的元素,迭代终止
 */
dir: function( elem, dir, until ) { // 这个函数很精髓,短短几行代码,支持遍历祖先、所有兄长、所有兄弟!
	var matched = [], // 匹配结果
		cur = elem[ dir ]; // 第一次访问,尝试在方向dir上取一次,为循环提供第一次判断(变量cur多余,只用elem就可满足)
		// 不包含自身
	/**
	 * 迭代访问,直到遇到document对象或遇到until匹配的元素
	 * cur.nodeType !== 9	当前DOM节点cur不是document对象
	 * !jQuery( cur ).is( until )	当前DOM节点cur不匹配表达式until
	 *
	 * until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )
	 * 这个布尔表达式也有点意思,执行最后的jQuery.is的隐含条件是:until !== undefined && cur.nodeType === 1
	 * 复合的布尔表达式和三元表达式,能减少代码行数、稍微提升性能,但是代码晦涩,不易阅读和维护。
	 * 也许看不懂也是jQuery风靡的原因之一
	 */
	while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
		if ( cur.nodeType === 1 ) { // 如果是Element,则放入匹配结果数组
			matched.push( cur );
		}
		cur = cur[dir]; // 将方向dir上的下一个节点,设置为当前节点,继续访问
	}
	return matched;
}

3. jQuery.nth( cur, result, dir, elem )

/**
 * 从一个元素出发,迭代检索某个方向上的第N个元素
 * cur	起始元素
 * result	第几个,1表示当前元素,2表示下一个,以此类推(搞不明白为什么从1开始?)
 * dir	迭代方向
 * elem	多余
 */
nth: function( cur, result, dir, elem ) {
	result = result || 1; // 第几个?默认从1开始?嗯,这里从1开始计数,1表示当前元素,2表示下一个,以此类推
	var num = 0; // 操蛋的初始值,初始为1更好理解
	// 下边的for循环看的蛋疼!
	// dir先在方向dir上取一次再循环,nth先循环一次然后再取
	// dir不包含自身,nth包含自身
	for ( ; cur; cur = cur[dir] ) { // 某个方向
		if ( cur.nodeType === 1 && ++num === result ) { // 先加后取值,num等于1时检查的是当前元素
			break;
		}
	}
	// 如果result为0或1,则取当前元素
	// 如果result小于0,则返回undefined
	return cur;
}

 

4. jQuery.nth( cur, result, dir, elem )

/**
 * 元素n的所有后续兄弟元素,包含n,不包含elem
 * n	起始元素(包含在返回结果中)
 * elem	排除元素
 */
sibling: function( n, elem ) {
	var r = [];
	for ( ; n; n = n.nextSibling ) { // 先判断再取,结果中包含n
		// nodeType === 1,只能是Element,过滤了其他的Text、Attr、Comment等元素
		if ( n.nodeType === 1 && n !== elem ) {
			r.push( n );
		}
	}
	return r;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注