jquery.jsを読み解く

第7回 jQueryライブラリ(1637行目~1794行目)

この記事を読むのに必要な時間:およそ 5 分

1704:       // We can get a speed boost by handling nth-child here
1705:       } else if ( m[1] == ":" && m[2] == "nth-child" ) {
1706:         var merge = {}, tmp = [],
1707:           // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
1708:           test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
1709:             m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||
1710:             !/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
1711:           // calculate the numbers (first)n+(last) including if they are negative
1712:           first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;
1713:  
1714:         // loop through all the elements left in the jQuery object
1715:         for ( var i = 0, rl = r.length; i < rl; i++ ) {
1716:           var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
1717: 
1718:           if ( !merge[id] ) {
1719:             var c = 1;
1720: 
1721:             for ( var n = parentNode.firstChild; n; n = n.nextSibling )
1722:               if ( n.nodeType == 1 )
1723:                 n.nodeIndex = c++;
1724: 
1725:             merge[id] = true;
1726:           }
1727: 
1728:           var add = false;
1729: 
1730:           if ( first == 0 ) {
1731:             if ( node.nodeIndex == last )
1732:               add = true;
1733:           } else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
1734:             add = true;
1735: 
1736:           if ( add ^ not )
1737:             tmp.push( node );
1738:         }
1739: 
1740:         r = tmp;
1741: 

1705行目からは,子要素フィルタのための処理で,:nth-child(2) のような書式を扱います。1708行目の正規表現は,'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'などの書式をパースして(first)n+(last)形式に変換し,firstおよびlastの数値をそれぞれ抽出します。'even'の場合は「2n⁠⁠,'odd'の場合は「2n+1」に変換してからパースします。

そして,1715行目のforループで要素を順に調べていきます。ここで出てくる変数mergeは,重複を無くすためのフラグです。1716行目でユニークなidを取得し,既に評価済みの要素の場合には,merge[id]をtrueにセットすることで二度同じ要素を評価しないようにしています。そして,1721行目のforループで子要素ノードを順にナンバリングしておきます。

1730行目ですが,firstが0の場合は,(first)n+(last)の式はlastと等しくなるので,先ほどナンバリングしたnodeIndex == lastならばaddフラグをtrueにしておきます。firstが0以外の場合は,(first)n+(last)の式を満たす場合にaddフラグをtrueに設定します。

1736行目にて,notがtrueかfalseかによってtmp配列に格納します。1740行目にてその配列をrに格納します。

1742:       // Otherwise, find the expression to execute
1743:       } else {
1744:         var f = jQuery.expr[m[1]];
1745:         if ( typeof f != "string" )
1746:           f = jQuery.expr[m[1]][m[2]];
1747: 
1748:         // Build a custom macro to enclose it
1749:         f = eval("false||function(a,i){return " + f + "}");
1750: 
1751:         // Execute it against the current filter
1752:         r = jQuery.grep( r, f, not );
1753:       }
1754:     }
1755: 
1756:     // Return an array of filtered elements (r)
1757:     // and the modified expression string (t)
1758:     return { r: r, t: t };
1759:   },
1760: 

以上のどのセレクタ式でもない場合ですが,jQuery.exprに該当するハッシュのキーを参照して,それが文字列でない場合はm[1]m[2]のキーを検索して変数 f に代入します。そして,1749行目にてeval()によりその式を返す関数を作成します。最後にjQuery.grep()メソッドによってその関数を要素配列に対して適用します。

最後に1758行目にて,r,tを持つハッシュ値を返して終了となります。

jQuery.dir()

1761:   dir: function( elem, dir ){
1762:     var matched = [];
1763:     var cur = elem[dir];
1764:     while ( cur && cur != document ) {
1765:       if ( cur.nodeType == 1 )
1766:         matched.push( cur );
1767:       cur = cur[dir];
1768:     }
1769:     return matched;
1770:   },
1771:   

jQuery.dirメソッドは,引数として指定されたelem要素のdirノードを順に辿っていき,要素の配列を返します。1763行目で,変数curに最初のelem[dir]の値を格納します。引数dirには"parentNode"や"nextSibling"などの値が渡されてくるので,1764行目のwhileループにて,目的のcur要素がなくなるまで,もしくはdocumentノードに到達するまで繰り返し処理を行い,matched配列に追加していきます。

jQuery.nth()

1772:   nth: function(cur,result,dir,elem){
1773:     result = result || 1;
1774:     var num = 0;
1775: 
1776:     for ( ; cur; cur = cur[dir] )
1777:       if ( cur.nodeType == 1 && ++num == result )
1778:         break;
1779: 
1780:     return cur;
1781:   },
1782:   

jQuery.nthメソッドは,cur要素からresult番目の要素を抽出します。dirには"nextSibling"や"previousSibling"などの値を指定して,指定された要素をどのように辿っていくかを指定します。例えば"nextSibling"が指定された場合には,curの兄弟要素を次々に辿っていくことになります。result番目の要素が見つかったら,1780行目でその要素を返します。

なお,第4引数elemは利用されていないようです。

jQuery.sibling()

1783:   sibling: function( n, elem ) {
1784:     var r = [];
1785: 
1786:     for ( ; n; n = n.nextSibling ) {
1787:       if ( n.nodeType == 1 && (!elem || n != elem) )
1788:         r.push( n );
1789:     }
1790: 
1791:     return r;
1792:   }
1793: });
1794: 

jQuery.siblingメソッドは,nの中から第2引数 elem 以外の兄弟要素を返します。1786行目のfor文で兄弟要素を順に辿っていき,elemが指定されている場合は,elem要素以外のものを配列にプッシュして返します。

著者プロフィール

山下英孝(やましたひでたか)

大学を卒業後,大手SIerに就職し,電機メーカーの研究所勤務を経て,ウノウに入社。1年半に渡ってWebサイトの開発,ディレクション,運用を経験した後に独立。2008年2月よりフリーエンジニアとして活動中。好きな言語はJavaScriptとPythonで,Linuxサーバ運用管理も得意。

ブログWeboo! Returns.