第二章 jQuery技術解密 (五)

mybwu_com發表於2013-12-28

2.3.5 引用 DOM 元素

jQuery() 函式能夠直接接受 HTML 字串,並把它們轉換為 DOM 結構,這是上一節中所講解的利用 jQuery() 函式生成 DOM 元素。當然,我們也可以看到 jQuery() 函式還可以接收 DOM 元素、DOM元素集合、HTML標籤或者 ID 值。下面我們就來分析 jQuery.fn.init() 構造器是如何把這些型別的引數轉換為 DOM 元素的。

對於 HTML 標籤來說,它使用 document.getElementsByTagName() 方法獲取 DOM 元素集合。對於 ID 引數來說,它使用 document.getElementById() 方法獲取特定的 DOM 元素。另外,還可以使用 DOM 元素的 childNodes、firstChild、lastChild、nextSibling、parentNode 和 previousSibling 的屬性引用 DOM 節點。

既然說 DOM 元素能夠通過 lastChild、parentNode 等屬性引用節點,jQuery 物件又是 DOM 元素的集合,因此,jQuery 就可以考慮通過整合 DOM 元素的這些屬性來獲得其集合中所有元素各自引用的節點。把這些間接引用的節點組合起來又構成了新的 DOM 元素集合。下面我們就來分析 jQuery 是如何引用節點的。

  1. //通過each()方法為jQuery物件對映一組引用DOM節點的方法
  2. jQuery.each({
  3. parent:function(elem){returnelem.parentNode;},//引用父節點
  4. parents:function(elem){returnjQuery.dir(elem,"parentNode");},//引用所有父節點
  5. next:function(elem){returnjQuery.nth(elem,2,"nextSibling");},//引用相鄰的下一個DOM元素
  6. prev:function(elem){returnjQuery.nth(elem,2,"previousSibling");},//引用相鄰的上一個DOM元素
  7. nextAll:function(elem){returnjQuery.dir(elem,"nextSibling");},//引用所有後繼DOM元素
  8. prevAll:function(elem){returnjQuery.dir(elem,"previousSibling");},//引用所有前繼DOM元素
  9. sibling:function(elem){returnjQuery.sibling(elem.parentNode.firstChild,elem);},//引用相鄰DOM元素
  10. children:function(elem){returnjQuery.sibling(elem.firstChild);},//引用所有子元素
  11. //如果存在iframe,則就是文件,或者所有子節點
  12. //elem.contentDocument||elem.contentWindow.documentiframe的屬性
  13. //http://devoloper.mozilla.org/on/docs/XUL:iframe
  14. contents:function(elem){returnjQuery.nodeName(elem,"iframe")?elem.contentDocument||
  15. elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
  16. },function(name,fn){
  17. //為jQuery物件註冊同名方法
  18. jQuery.fn[name]=function(selector){
  19. //每個元素都執行同名方法
  20. varret=jQuery.map(this,fn);
  21. //過濾元素集
  22. if(selector&&typeofselector=="string")
  23. ret=jQuery.multiFilter(selector,ret);
  24. //返回構建的jQuery物件
  25. returnthis.pushStack(jQuery.unique(ret),name,selector);
  26. };
  27. });
在上面的程式碼中,為 jQuery 物件繫結了一組方法,這些方法能夠引用不同位置的節點,主要包括父節點、子節點和兄弟節點三類。

  • 對於父節點引用來說,可以引用當前 DOM 元素的父親節點,也可以獲得所有父級節點,包括祖先節點。
  • 對於子節點引用來說,就是可以引用所有直接的子節點,但不包括不相鄰的後代節點。
  • 對於兄弟節點引用來說,可以引用包括當前 DOM 元素的前或後相鄰節點,也包括前後相近的所有元素。
jQuery 通過 jQuery.each() 公共函式把這個節點引用的方法註冊到 jQuery.fn 原型物件中,即 jQuery 物件的同名方法中。因為 jQuery 物件的 DOM 元素是一個集合,所以就必須對集合中每個 DOM 元素執行相同的操作,也就是說為每個 DOM 元素呼叫屬性包含的函式,如 parent: function(elem){return elem.parentNode;}; 中的 function(elem){return elem.parentNode;} 。
當然,在引用的節點中,還包括很多重複的 DOM 元素,或者使用者需要過濾的其他節點,這些操作都需要利用過濾函式進行過濾,關於這個話題將在 CSS 選擇器一節中進行詳細的講解。
在上面定義的 jQuery 物件方法中,jQuery 也提供了幾個公共函式: jQuery.dir()、jQuery.nth() 和 jQuery.sibling() 來輔助完成引用 DOM 節點。下面我們來分析這幾個公共函式的用法。

  1. //從一個元素出發,檢索某個方向上的所有元素
  2. //如可以把元素的parentNode、nextSibling、previousSibling、lastChild、firstChild屬性作為方向
  3. //引數說明:
  4. //elem參數列示起點元素
  5. //dir參數列示元素的方向屬性,如parentNode、nextSibling、previousSibling、lastChild、firstChild
  6. jQuery.dir=function(elem,dir){
  7. varmatched=[],cur=elem[dir];
  8. //逐級迭代訪問,直到訪問到document節點
  9. while(cur&&cur!=document){
  10. if(cur.nodeType==1)//Element型別
  11. matched.push(cur);
  12. cur=cur[dir];//向上一級傳遞節點
  13. }
  14. returnmatched;
  15. };
dir 是 direction(方向) 一詞的縮寫,表示朝一個方向一直迭代到盡頭。例如,parentNode 能夠把父節點作為當前節點,再取其父節點,通過這種方式可以迭代到 document 物件為止。另外,對於 nextSibling、previousSibling、lastChild 和 firstChild 元素屬性都具有方向性,因此只要獲取元素具有 dir (方向) 特性的屬性,就可以反覆迭代讀取。每迴圈一次都會把取到的元素儲存起來。

所以說,dir() 函式對於檢索 DOM 文件樹中呈放射線性排列的元素來說,是非常有用的。但是如果要檢索在某個方向上的第幾個元素,如檢索偶數序號位置的元素,就需要使用 nth() 函式。該函式的原始碼如下所示。

  1. //從一個元素出發,檢索某個方向上的第幾個元素。引數Result是第幾個
  2. //引數說明:
  3. //cur參數列示起點元素
  4. //dir參數列示元素的方向屬性,如parentNode、nextSibling、previousSibling、lastChild和firstChild
  5. //result參數列示級數,預設值為1
  6. //elem引數是一個無用引數
  7. jQuery.nth=function(cur,result,dir,elem){
  8. result=result||1;
  9. varnum=0;
  10. for(;cur;cur=cur[dir])
  11. if(cur.nodeType==1&&++num==result)
  12. break;
  13. returncur;
  14. };
jQuery.nth() 函式與 jQuery.dir() 函式在設計思路上是完全相同的,但是 jQuery.dir() 函式不包含自身,而 jQuery.nth() 函式可以包含自身,如果 jQuery.nth() 函式的引數 result 等於 1,則返回自身元素。如果沒有找到元素則返回空,如 undifined 或 null 。

jQuery.sibling() 函式就比較簡單,但是沒有上面兩個方法有用。它實現了從一個元素(包括自身)檢索所有後續相鄰元素,然後再從這個後續的相鄰元素排除一個指定元素。例如:

  1. //從包含引數n的元素開始檢索所有後續相鄰元素,但不包含引數elem指定的元素
  2. //引數說明:
  3. //n參數列示起點元素
  4. //elem引數排除元素
  5. jQuery.sibling=function(n,elem){
  6. varr=[];
  7. for(;n;n=n.nextSibling){
  8. if(n.nodeType==1&&n!=elem)
  9. r.push(n);
  10. }
  11. returnr;
  12. };

相關文章