前言
内容主要是跟着慕课网上的jQuery源码解析系列课程以及自己的实践+理解来写的,可能会有错误,欢迎指出^_^。
节点遍历
遍历的接口可以分为4个类型:
- 祖先
- 同胞兄弟
- 后代
- 过滤
所以这个章节的核心内容就是关于如何通过遍历获取到指定的节点的。
获取父辈/祖先节点
主要集中在三个函数:
- parent():获得当前匹配元素集合中每个元素的父元素
- parents():获得当前匹配元素集合中每个元素的祖先元素
- parentsUntil():获得当前匹配元素集合中每个元素的祖先元素,直到(但不包括)被选择器、DOM 节点或 jQuery 对象匹配的元素
因为实际上这三个要达到的目的的手段有相同的部分,所以jQuery把这个环节抽出来写成了一个dir函数,以减少重复代码,模拟实现过程如下:
/* 寻找所有符合要求的节点
* elem:目标元素
* dir:目标类型(如:parentNode)
* until:搜索终止元素 */
function dir(elem, dir, until) {
/* matched:符合条件的元素
tuncate:搜索终点 */
var matched = [],
truncate = until !== undefined;
/* nodeType == 9,表示Document
当元素存在,并且元素不是Document
dir=="parentNode",则一层层遍历它的parentNode */
while ((elem = elem[dir]) && elem.nodeType !== 9) {
//nodeType == 1,表示节点为元素
if (elem.nodeType === 1) {
//如果存在搜索终点(标签名|类名),退出查找
if (truncate) {
if (elem.nodeName.toLowerCase() == until || elem.className == until) {
break;
}
}
//否则加入集合
matched.push(elem);
}
}
return matched;
}
补充点:关于$.each()方法
$.each(Object, function(name, value) {
this; //this指向当前属性的值
name; //name表示Object当前属性的名称
value; //value表示Object当前属性的值
});
$.each(Array, function(i, value) {
this; //this指向当前元素
i; //i表示Array当前下标
value; //value表示Array当前元素
});
我们肯定用过$(“…”).each(fn)方法来遍历对象,但是还有一个$.each方法,可以遍历对象和数组。要把parent三个方法扩充到jQuery里面,就用了这个方法:
jQuery.each({
parent: function(elem) {
var parent = elem.parentNode;
//如果父节点存在,且不是文档碎片,就返回。
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function(elem) {
//通过dir函数获取所有祖宗节点
return dir(elem, "parentNode");
},
parentsUntil: function(elem, until) {
return dir(elem, "parentNode", until);
}
}, function(name, fn) {
/* name:索引值
* fn:属性值 */
ajQuery[name] = function(selector,until) {
return fn(selector.until);
};
});
补充点:关于nodeType
具体请参阅W3CSchool,这里列一些比较常见的:
1:Element,代表元素
2:Attr,代表属性
3:Text,代表元素或属性中的文本内容。
8:Comment,代表注释。
9:Document,代表整个文档(DOM 树的根节点)。
11:DocumentFragment,代表轻量级的 Document 对象,能够容纳文档的某个部分
获取同胞节点
主要是五个方法:
- nextAll():匹配元素集合中每个元素的所有跟随的同胞元素。
- prevAll():当前匹配元素集合中每个元素的前面的同胞元素。
- nextUntil()
- prevUntil()
- siblings():获得匹配集合中每个元素的同胞(不包括自己)。
有了dir函数之后,就可以充分利用这个函数了。
function nextAll(elem) {
return dir(elem, "nextSibling");
}
function prevAll(elem) {
return dir(elem, "previousSibling");
}
function nextUntil(elem, until) {
return dir(elem, "nextSibling", until);
}
function prevUntil(elem, until) {
return dir(elem, "previousSibling", until);
}
function siblings(n, elem) {
var matched = [];
for (; n; n = n.nextSibling) { //如果存在下一个兄弟节点
if (n.nodeType === 1 && n !== elem) {
//是元素节点,并且不包括自己
matched.push(n);
}
}
return matched;
}
这里siblings的模拟实现思想是获取目标元素的父亲节点,然后遍历父亲节点的直接子节点来获取,碰到了目标元素则跳过不加入。