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') returnvalue(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') returnvalue(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...
(完)