jQuery的遍歷結構設計之遍歷祖先

進擊的小進進發表於2019-03-22

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>

複製程式碼

githubgithub.com/AttackXiaoJ…


jQuery的遍歷結構設計之遍歷祖先

(完)

相關文章