【筆記】jQuery原始碼(節點遍歷)

MOCHIKO發表於2018-03-25

前言

內容主要是跟著慕課網上的jQuery原始碼解析系列課程以及自己的實踐+理解來寫的,可能會有錯誤,歡迎指出^_^。

節點遍歷

遍歷的介面可以分為4個型別:

  1. 祖先
  2. 同胞兄弟
  3. 後代
  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的模擬實現思想是獲取目標元素的父親節點,然後遍歷父親節點的直接子節點來獲取,碰到了目標元素則跳過不加入。

相關文章