前言:可以先自己動手做 三
,然後回頭看 一、二
一、遍歷歸類 遍歷的介面可以歸為幾大類: (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:github.com/AttackXiaoJ…
(完)