您的位置:首页 > Web前端 > JQuery

jQuery源码分析-11 DOM遍历-Traversing-DOM遍历方法

2014-10-11 16:46 826 查看
作者:nuysoft/高云 QQ:47214707 Email:nuysoft@gmail.com

声明:本文为原创文章,如需转载,请注明来源并保留原文链接。
jQuery源码分析系列(持续更新)

1. 属性childNodes vs 属性children
childNodesRetrieves a collection of HTML Elements and TextNode objects that are direct descendants of the specified object.

标准,包含HTML元素、文本
childrenRetrieves a collection of DHTML Objects that are direct descendants of the object.

非标准,只包含HTML元素
2. 方法children vs 方法contents
jQuery.fn.children( elem )只包含Element
jQuery.fn.contents( elem )包含Element、Text、Comment
3. 完整源码分析(亮点在排序、去重部分)
jQuery.each({
parent: function( elem ) { // 父元素
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null; // DocumentFragment 11, DocumentType 10
},
parents: function( elem ) { // 祖先元素
return jQuery.dir( elem, "parentNode" ); // 检索所有父元素,直至document
},
parentsUntil: function( elem, i, until ) { //
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) { // 下一个兄弟元素
return jQuery.nth( elem, 2, "nextSibling" ); // 从1开始计数,当前是第1个,为什么要再搞一套不一致的索引方法呢?
},
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 ); // 父元素的第一个子元素的所有兄弟元素,排除当前元素elem
// 取子元素childNodes然后过滤elem,效率更高
},
children: function( elem ) { // 所有的子节点,只包含Element
return jQuery.sibling( elem.firstChild ); // 第一个子元素的所有兄弟元素
// 为什么不直接取子元素呢?childNodes不是效率更高么?
},
contents: function( elem ) { // 所有的子节点,包含Element、Text、Comment
return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document : // 如果是iframe,则取document
jQuery.makeArray( elem.childNodes ); // 将childNodes转换数组,childNodes是伪数组,转换后遍历数组时可以避免对childNodes进行检查,提高性能
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until ), // 将this中的元素,用fn处理,最后返回真正的数组,until用于过滤
// The variable 'args' was introduced in
// https://github.com/jquery/jquery/commit/52a0238 // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
// http://code.google.com/p/v8/issues/detail?id=1050 args = slice.call(arguments); // 将arguments复制并转为数组

if ( !runtil.test( name ) ) { // 不以Until结尾
selector = until; // 不需要参数until,只有一个参数selector,util只到这里为止
}

if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret ); // 对ret数组用selector进行过滤,只留下匹配的元素
// jQuery.filter会调用jQuery.find.matches > Sizzle.matches > Sizzle,Sizzle查找、过滤的结果已经经过排序、去重
}

/**
* 排序、去重
* 首先要知道this是经过排序、且无重复
* 如果长度大于1,才需要判断是否需要去重
* 如果name是 children contents next prev 之一,则不需要排序去重,因为这四个方法不会产生无序和重复的结果,为什么呢?
* 因为this中的元素是有序、去重的,所以的它子节点、兄弟、兄长,也都是有序、无重复的
* 但是parent parents parentsUntil nextAll prevAll nextUntil prevUntil siblings却有可能产生重复、无序的元素
*
* 可见这里进行了优化,不可否则这种编码习惯很精致很赞,但是:
* 即使不需要排序、去重的情况,也必然要经过这些漫长的判断,就是一种浪费
* 这种多功能导致的复杂性和无谓的性能消耗,在jQuery中随处可见
* 虽然没有做详尽的性能测试,其实也没必要,但稍微给我有点“机关算尽太聪明,枉费了卿卿性命”的感觉
* 从刚开始读源码时的惊艳和崇拜,渐渐的能分辨其中的精髓与作者的权衡拿捏,jQuery的伟大不可否认,
* 但是我们写代码的时候,要借鉴参考,也要思考取舍
*
* 上边的感触仅供参考,我还处于能读懂和能分析jQuery源码的阶段,不到自创一套类库的境界,
* 也许水平提高了jQuery不能满足我的时候,再回头看,兴许是另一重感悟了。
*/
ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; // 去重

// rparentsprev = /^(?:parents|prevUntil|prevAll)/
// 因为前边返回的结果是按照元素在文档中的位置顺序返回的,遇到rparentsprev则需要再反过来,方便使用
if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
ret = ret.reverse(); // 倒序
}

return this.pushStack( ret, name, args.join(",") ); // 构造jQuery对象
};
})

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: