jQuery的DOM遍歷模塊對DOM模型的原生屬性parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling進行了封裝和擴展,用於在DOM樹中遍歷父元素、子元素和兄弟元素。 可以通過jQuery的實例來訪問,方法如下: ...
jQuery的DOM遍歷模塊對DOM模型的原生屬性parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling進行了封裝和擴展,用於在DOM樹中遍歷父元素、子元素和兄弟元素。
可以通過jQuery的實例來訪問,方法如下:
- parent() ;獲取匹配元素的父元素
- parents(selector) ;獲取匹配元素的所有祖先元素 ;selector用於過濾查找的元素,如果為空則獲取所有的祖先元素
- parentsUntil(until,selector) ;獲得當前匹配元素集合中每個元素的祖先元素,直到遇到until元素
- next() ;獲取匹配元素之後緊挨著的兄弟元素,沒有則返回undefined
- nextAll(selector) ;獲取匹配元素之後緊挨著的所有兄弟元素
- nextUntil(until,selector) ;獲取匹配元素之後緊挨著的所有兄弟元素,直到遇到until元素
- prev() ;獲取匹配元素之前緊挨著的兄弟元素,沒有則返回undefined
- prevAll(selector) ;獲取匹配元素之前緊挨著的所有兄弟元素
- prevUntil(until,selector) ;獲取匹配元素之前緊挨著的所有兄弟元素,直到遇到until元素
- siblings(selector) ;獲取匹配元素的所有兄弟元素,如果指定了selector則只獲取該類型的元素
- children(selector) ;獲取匹配元素的子元素,不包括文本節點和註釋節點
- contents() ;獲取匹配元素的子元素,包括文本節點和註釋節點
舉個慄子:
writer by:大沙漠 QQ:22969969
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script> </head> <body> <div> <h1>Hello World!</h1> <p>123</p> <span>123</span> </div> <script> console.log( $('p').parent() ) //獲取p元素的父元素 console.log( $('p').prev() ) //獲取p元素之前緊挨的兄弟元素 console.log( $('p').next() ) //獲取p元素之後緊挨的兄弟元素 console.log( $('p').siblings() ) //獲取p元素的所有兄弟元素 console.log( $('div').children() ) //獲取div的所有子元素,不包括文本節點和註釋節點 console.log( $('div').contents() ) //獲取div的所有子元素,包括文本節點和註釋節點 </script> </body> </html>
輸出如下:
對應慄子里的每一個輸出,分別輸出
- p元素的父節點
- p元素的上一個兄弟元素
- p元素的下一個兄弟元素
- p元素的所有兄弟元素
- div元素過濾掉文本節點和空白節點的子節點
- div元素包括文本節點和空白節點的子節點
返回的都是一個jQuery對象,非常的好用,操作DOM時都會用到這幾個方法
源碼分析
jQuery對於DOM遍歷模塊的實現是基於三個工具函數實現的
- $.dir(elem, dir, until) ;從elem元素開始,查找dir方向(可以是:parentNode、nextSibling或previousSibling)上的所有元素,如果在查找過程中遇到匹配參數until的元素,則查找終止
- $.nth(cur, result, dir, elem) ;從cur元素出發,查找dir方向(parentNode、nextSibling或previousSibling)上的第result個元素
- $.sibling(n, elem) ;負責查找一個元素之後的所有兄弟元素,包括起始元素,但不包括參數elem。n是查找的起始元素,包含在結果集中,elem是可選的DOM元素,不包含在返回結果中。
舉個慄子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script> </head> <body> <div> <h1>Hello World!</h1> <p>123</p> <span>123</span> </div> <script> var h1 = document.getElementsByTagName('h1')[0], //分別獲取h1、p、span元素的節點引用 p = document.getElementsByTagName('p')[0], span = document.getElementsByTagName('span')[0]; console.log( $.dir(h1,'parentNode') ) //列印h1的所有祖先節點 console.log( $.nth(h1,3,'nextSibling') ) //獲取h1之後的第二個節點 console.log( $.sibling(h1,h1) ) //獲取h1的所有兄弟元素,預設獲取包括自身在內的元素,參數2也傳入h1表示排除h1元素 </script> </body> </html>
輸出如下:
輸出三行,分別對應h1的所有祖先節點、h1之後的第二個子節點和h1的所有兄弟元素,和DOM樹中也是對應的。
對於$.dir、$.nth和$.sibling來說,它的實現如下:
jQuery.extend({ dir: function( elem, dir, until ) { //從elem元素開始,查找dir方向(可以是:parentNode、nextSibling或previousSibling)上的所有元素,如果在查找過程中遇到匹配參數until的元素,則查找終止。 var matched = [], cur = elem[ dir ]; //cur表示下一個元素,根據參數dir不同,可能是父元素、前一個兄弟元素 或者 後一個兄弟元素 while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { //如果下一個元素存在 且 不是document對象(nodeType等於9) 則繼續判斷括弧里的語句,否則跳出迴圈 括弧內:如果沒有傳入until參數,或者傳入了until參數並且當前元素不是Element節點(屬性nodeType等於1) 或者 傳入了until參數且當前參數是Element節點且該元素不匹配參數until 則進行迴圈 if ( cur.nodeType === 1 ) { //如果cur是元素節點 matched.push( cur ); //存儲到數組matched中 } cur = cur[dir]; //獲取下一個方向上的元素 } return matched; //返回找到了的Element節點數組。 }, nth: function( cur, result, dir, elem ) { //從cur元素出發,查找dir方向(parentNode、nextSibling或previousSibling)上的第result個元素 result = result || 1; var num = 0; //當前查找的元素序號 for ( ; cur; cur = cur[dir] ) { //從起始元素cur觸發,沿著方向dir迭代遍歷,只要cur元素存在則一直查找,直到查找該方向上的第result個Element節點 if ( cur.nodeType === 1 && ++num === result ) { //如果cur是一個元素節點,則num+1,此時如果num等於result,則表示查找到該元素了,則跳出迴圈 break; } } return cur; //返回查找到的cur元素。 }, sibling: function( n, elem ) { //負責查找一個元素之後的所有兄弟元素,包括起始元素,但不包括參數elem。n是查找的起始元素,包含在結果集中,elem是可選的DOM元素,不包含在返回結果中。 var r = []; for ( ; n; n = n.nextSibling ) { //從起始元素n觸發,迭代遍歷其後的所有兄弟元素 if ( n.nodeType === 1 && n !== elem ) { //如果該兄弟元素是元素節點且不等於參數elem則被放入數組r中。 r.push( n ); } } return r; //返回存放了找到的Element節點的數組r } });
也就是對parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling這些原生DOM操作的一些封裝
我們通過jQuery實例可以訪問的方法的實現如下:
jQuery.each({ parent: function( elem ) { //返回匹配元素的父元素 var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; //如果父元素存在且不是文檔碎片元素,則返回該父元素,否則返回null }, parents: function( elem ) { //返回匹配元素的所有祖先元素 return jQuery.dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { //獲得當前匹配元素集合中每個元素的祖先元素,直到遇到until元素 return jQuery.dir( elem, "parentNode", until ); }, next: function( elem ) { //獲取匹配元素之後緊挨著的兄弟元素 return jQuery.nth( elem, 2, "nextSibling" ); }, prev: function( elem ) { //獲取匹配元素之前緊挨著的兄弟元素 return jQuery.nth( elem, 2, "previousSibling" ); }, nextAll: function( elem ) { //獲取匹配元素之後緊挨著的所有兄弟元素 return jQuery.dir( elem, "nextSibling" ); }, prevAll: function( elem ) { //獲取匹配元素之前緊挨著的所有兄弟元素 return jQuery.dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { //獲取匹配元素之後緊挨著的所有兄弟元素 ,直到遇到匹配元素until為止 return jQuery.dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { //獲取匹配元素之前緊挨著的所有兄弟元素 ,直到遇到匹配元素until為止 return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { //獲取匹配元素的所有兄弟元素 return jQuery.sibling( elem.parentNode.firstChild, elem ); }, children: function( elem ) { //獲取匹配元素的子元素 return jQuery.sibling( elem.firstChild ); }, contents: function( elem ) { //獲取匹配元素的子元素,包括文本節點和註釋節點 return jQuery.nodeName( elem, "iframe" ) ? elem.contentDocument || elem.contentWindow.document : jQuery.makeArray( elem.childNodes ); } }, function( name, fn ) { //模板函數,用於調用對應的遍歷函數查找DOM元素,然後執行過濾、排序、和去重操作 name是每個函數名,fn是對應的值(也就是函數) jQuery.fn[ name ] = function( until, selector ) { //定義模版函數,真正是在這裡定義的,它接收兩個參數 until:選擇器表達式,用於指示查找停止的位置。selector:選擇器表達式,用於過濾找到的元素 var ret = jQuery.map( this, fn, until ); //調用方法jQuery.map()遍歷當前匹配元素集合,對每個元素調用遍歷函數fn,並將遍歷函數fn的返回值放入一個新的數組ret中。 if ( !runtil.test( name ) ) { //如果遍歷函數名不以'Until'結尾的,則最多只有一個參數 selector = until; //修正參數selector為until } if ( selector && typeof selector === "string" ) { //如果設置了參數selector 且selector是一個字元串 ret = jQuery.filter( selector, ret ); //則調用jQuery.filter()對數組ret中的DOM元素逐個過濾,最終只保留匹配選擇器表達式selector的元素。 } ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { //排序 ret = ret.reverse(); } return this.pushStack( ret, name, slice.call( arguments ).join(",") ); //創建一個jQuery對象並返回 }; });
實現就是通過$.each()遍歷一個對象,依次執行參數2這個函數,該函數會在$.fn上依次掛載每個介面,使用$.map()工具方法它會依次執行fn函數,進行一些過濾去重後最後調用$.pushStack()將數組轉換為一個jQuery對象,這裡理解起來有點難度,需要多理一下代碼。