jQuery的遍历结构设计之遍历祖先

前言:可以先自己动手做 三,然后回头看 一、二

一、遍历归类遍历的接口可以归为几大类:(1)祖先(2)同胞兄弟(3)后代(4)过滤
比如说 祖先的遍历接口:有从目标节点向上遍历单个父节点的 parent();有从目标节点向上遍历所有祖先节点的 parents();有从目标节点向上遍历到另一目标节点的 parentsUntil()
这些遍历的接口不可能去一一实现,而是功能类似的接口,用统一的方法去封装。
比如针对层级关系的处理,jQuery是用 dir 方法实现的,如下所示:
// 针对层级关系的处理,jQuery就抽出了一个dir的方法,用于根据传递的元素与词素的位置关系,查找指定的元素。
// parent,parents,parentsUntil等方法如代码所示:

// parents:dir(elem,”parentNode”)
//parentsUntil:dir(elem, “parentNode”, until)
function dir(elem, dir, until) {
let matched = []
//parents:false
let truncate = until !== undefined;
//elem默认值为elem[dir]
//举例:
//let a=<li class=”item-1″>1</li>
//a.parentNode=<ul class=”level-2″>xxx</ul>

//9是Document,即整个文档的根节点
//即递归的终结条件是到document结束
while ((elem = elem[dir]) && elem.nodeType !== 9) {
//1 Element即一个元素
//意思就是elem是一个Element
if (elem.nodeType === 1) {
//parents方法不走这边
//parentsUntil方法走这边
//body
if (truncate) {
//在向上递归返回父节点的同时,判断是否达到条件
//节点名和类名
if (elem.nodeName.toLowerCase() === until || elem.className === until) {
break;
}
}
matched.push(elem);
}
}

//写法二
//elem由选择器判断,是必存在才会运行到此处的
// while (elem.nodeType !== 9) {
// elem = elem[dir]
// xxx
// xxx
// }

return matched;
}

二、迭代器迭代器可以理解为一个遍历方法,该方法的第一个参数是一个 object,该对象的每个属性都是一个 function;该方法的第二个参数是一个 callback,该回调函数能够按顺序处理 object 的每个 function,并且不暴露 object 的内部。
这也是设计模式中的迭代器模式。
jQuery的迭代器除了遍历外,还会将相同功能的代码合并处理:
let ajQuery = {};
//循环自定义一个对象,对象的每个属性的value是function,并在回调中对key、value进行操作
//jQuery.each即$.each
//$.each(object,callback(key,value))
//$.each(array,callback(index,value))
//详情请参考:http://api.jquery.com/jquery.each/
jQuery.each({
//本质即 elem.parentNode
parent: function(elem) {
//调用原生js的parentNode
var parent = elem.parentNode;
console.log(elem,’elem87′)
//11表示documentFragment
//documentFragment是没有父节点parentNode的!!
//详情请看:深入理解DOM节点类型第四篇——文档片段节点DocumentFragment
// https://www.cnblogs.com/xiaohuochai/p/5816048.html
return parent && parent.nodeType !== 11 ? parent : null;
},

//parents的本质即利用 elem.parentNode 向上递归直到document节点
parents: function(elem) {
return dir(elem, “parentNode”);
},
//该方法从父元素向上遍历 DOM 元素的祖先,直至文档根元素的所有路径,直到到达指定的元素为止
//until既可以是节点名也可以是类名
parentsUntil: function(elem, until) {
return dir(elem, “parentNode”, until);
}
},
//回调函数
//对象:key value
//数组:index value
function(key, value) {
console.log(key,value,’name107′)
//将jQuery的方法定义到ajQuery上
ajQuery[key] = function(elem, until) {
console.log(elem,until,’selector116′)
return value(elem, until);
};
});

三、轮到你了关键:
//element表示原生DOM节点$().parent 本质即 elem.parentNode$().parents 的本质即利用 elem.parentNode 向上递归直到 document 节点$(). parentsUntil 的本质即 elem.nodeName.toLowerCase() === 另一目标节点名 || elem.className === 另一目标节点类名

完整示例代码:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<title>jQuery的遍历结构设计</title>
</head>
<body>
<script src=”jQuery.js”></script>
<button id=”test1″>模拟遍历祖先</button>

<button id=”test2″>jQuery遍历祖先</button>

<ul class=”level-1″>
<li class=”item-i”>I</li>
<li class=”item-ii”>II
<ul class=”level-2″>
<li class=”item-a”>A</li>
<li class=”item-b”>B
<ul class=”level-3″>
<li class=”item-1″>1</li>
<li class=”item-2″>2</li>
<li class=”item-3″>3</li>
</ul>
</li>
<li class=”item-c”>C</li>
</ul>
</li>
<li class=”item-iii”>III</li>
</ul>

<script>
// 针对层级关系的处理,jQuery就抽出了一个dir的方法,用于根据传递的元素与词素的位置关系,查找指定的元素。
// parent,parents,parentsUntil等方法如代码所示:

// parents:dir(elem,”parentNode”)
//parentsUntil:dir(elem, “parentNode”, until)
function dir(elem, dir, until) {
let matched = []
//parents:false
let truncate = until !== undefined;
//elem默认值为elem[dir]
//举例:
//let a=<li class=”item-1″>1</li>
//a.parentNode=<ul class=”level-2″>xxx</ul>

//9是Document,即整个文档的根节点
//即递归的终结条件是到document结束
while ((elem = elem[dir]) && elem.nodeType !== 9) {
//1 Element即一个元素
//意思就是elem是一个Element
if (elem.nodeType === 1) {
//parents方法不走这边
//parentsUntil方法走这边
//body
if (truncate) {
//在向上递归返回父节点的同时,判断是否达到条件
//节点名和类名
if (elem.nodeName.toLowerCase() === until || elem.className === until) {
break;
}
}
matched.push(elem);
}
}

//写法二
//elem由选择器判断,是必存在才会运行到此处的
// while (elem.nodeType !== 9) {
// elem = elem[dir]
// xxx
// xxx
// }

return matched;
}

let ajQuery = {};
//循环自定义一个对象,对象的每个属性的value是function,并在回调中对key、value进行操作
//jQuery.each即$.each
//$.each(object,callback(key,value))
//$.each(array,callback(index,value))
//详情请参考:http://api.jquery.com/jquery.each/
jQuery.each({
//本质即 elem.parentNode
parent: function(elem) {
//调用原生js的parentNode
var parent = elem.parentNode;
console.log(elem,’elem87′)
//11表示documentFragment
//documentFragment是没有父节点parentNode的!!
//详情请看:深入理解DOM节点类型第四篇——文档片段节点DocumentFragment
// https://www.cnblogs.com/xiaohuochai/p/5816048.html
return parent && parent.nodeType !== 11 ? parent : null;
},

//parents的本质即利用 elem.parentNode 向上递归直到document节点
parents: function(elem) {
return dir(elem, “parentNode”);
},
//该方法从父元素向上遍历 DOM 元素的祖先,直至文档根元素的所有路径,直到到达指定的元素为止
//until既可以是节点名也可以是类名
parentsUntil: function(elem, until) {
return dir(elem, “parentNode”, until);
}
},
//回调函数
//对象:key value
//数组:index value
function(key, value) {
console.log(key,value,’name107′)
//将jQuery的方法定义到ajQuery上
ajQuery[key] = function(elem, until) {
console.log(elem,until,’selector116′)
return value(elem, until);
};
});

$(“#test1″).click(function() {
// <li class=”item-1”>1</li>
var item = document.querySelectorAll(‘.item-1′)[0]
// console.log(item,’item93’)
//这他妈太神奇了。。
console.log(item[‘parentNode’],’item94′)
console.log(item[‘className’],’item95′)
console.log(item.parentNode,’item101′)
console.log(item.parentNode.nodeName,’item117′)
console.log(item.parentNode.className,’item118′)
console.log(ajQuery.parent(item))
console.log(ajQuery.parents(item).length)
console.log(ajQuery.parentsUntil(item, ‘body’).length)
})

$(“#test2”).click(function() {
var item = $(‘.item-1’)
console.log(item)
console.log(item.parent()[0])
console.log(item.parents().length)
console.log(item.parentsUntil(‘body’).length)
})
</script>
</body>
</html>

github:https://github.com/AttackXiao…

(完)

评论

发表回复

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

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理