jQueryソース——.html()メソッドの原理解析
20914 ワード
文字列をhtmlフラグメントに変換する場合、文字列はコンテナのinnerHTMLプロパティとして割り当てられます.しかし、innerHTMLには多くの限界があります.たとえば、変換したい文字列にラベルがあり、すぐに実行される関数が含まれています.この文字列をinnerHTMLを介してhtmlフラグメントに変換すると、ラベルの関数は実行されません.
jQueryのhtml()関数はinnerHTMLの欠陥を補うことができ,この方法がどのように実現されているかを見る.
実は原理は簡単です:正則はラベルに一致して、js関数を取得して、それからeval()関数で解析します.jQueryはこの工事を処理する上でいくつかの細部を学ぶ価値がある.
まずhtml()関数のメインエントリを見てみましょう.
1.html()関数は、作用域の汚染を避けるために、単一の閉包access()関数を返す.
2.14行目、valueがstringタイプであることをまず決定し、rnoInnerhtmlを用いる.test(value)正則マッチングvalueにラベルが含まれているかどうか.
3.26行目は、まずコンテナの内容をクリーンアップし、valueをコンテナのinnerHTML属性として付与し、次にコンテナを表すローカル変数elemを0に付与し、37行の論理をスキップする.一部の同窓会では、elemを0に割り当てるのはなぜdom要素に影響しないのかと疑問に思っている.JavaScriptの値タイプと参照タイプの違いについては、自分で関連資料を参照してください.
4.38行目、valueにタグが含まれている場合、append()メソッドで後続操作を行う.
ではappend()関数はどのように処理されているのでしょうか.
append()関数はdomManip()関数を呼び出し、コールバック関数のパラメータelemはdomManip()関数で処理されたdocumentFragmentであり、domManip()の内部コードは以下の通りである.
1.28行目-55行目、docmentFragmentを生成し、後続の解析実行のためにノードをクローニングする.
2.57行目-79行目、内部のコードを実行する、75行のnodeに注意する.text || node.textContent || node.innerHTML|",タグ内部のテキストを取得する互換書き方です.
3.69行目、タグが外部リソースを参照している場合、リソースurlを要求する.
3.75行目、タグが行内コードである場合、globaleEval()関数を呼び出して内部の論理を実行し、コードは以下の通りである.
jQueryのhtml()関数はinnerHTMLの欠陥を補うことができ,この方法がどのように実現されているかを見る.
実は原理は簡単です:正則は
まずhtml()関数のメインエントリを見てみましょう.
1 html: function( value ) {
2 return access( this, function( value ) {
3 var elem = this[ 0 ] || {},
4 i = 0,
5 l = this.length;
6
7 if ( value === undefined ) {
8 return elem.nodeType === 1 ?
9 elem.innerHTML.replace( rinlinejQuery, "" ) :
10 undefined;
11 }
12
13 // See if we can take a shortcut and just use innerHTML
14 if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
15 ( support.htmlSerialize || !rnoshimcache.test( value ) ) &&
16 ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
17 !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
18
19 value = value.replace( rxhtmlTag, "<$1></$2>" );
20
21 try {
22 for (; i < l; i++ ) {
23 // Remove element nodes and prevent memory leaks
24 elem = this[i] || {};
25 if ( elem.nodeType === 1 ) {
26 jQuery.cleanData( getAll( elem, false ) );
27 elem.innerHTML = value;
28 }
29 }
30
31 elem = 0;
32
33 // If using innerHTML throws an exception, use the fallback method
34 } catch(e) {}
35 }
36
37 if ( elem ) {
38 this.empty().append( value );
39 }
40 }, null, value, arguments.length );
41 },
1.html()関数は、作用域の汚染を避けるために、単一の閉包access()関数を返す.
2.14行目、valueがstringタイプであることをまず決定し、rnoInnerhtmlを用いる.test(value)正則マッチングvalueに
3.26行目は、まずコンテナの内容をクリーンアップし、valueをコンテナのinnerHTML属性として付与し、次にコンテナを表すローカル変数elemを0に付与し、37行の論理をスキップする.一部の同窓会では、elemを0に割り当てるのはなぜdom要素に影響しないのかと疑問に思っている.JavaScriptの値タイプと参照タイプの違いについては、自分で関連資料を参照してください.
4.38行目、valueに
ではappend()関数はどのように処理されているのでしょうか.
1 append: function() {
2 return this.domManip( arguments, function( elem ) {
3 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
4 var target = manipulationTarget( this, elem );
5 target.appendChild( elem );
6 }
7 });
8 },
append()関数はdomManip()関数を呼び出し、コールバック関数のパラメータelemはdomManip()関数で処理されたdocumentFragmentであり、domManip()の内部コードは以下の通りである.
1 domManip: function( args, callback ) {
2
3 // Flatten any nested arrays
4 args = concat.apply( [], args );
5
6 var first, node, hasScripts,
7 scripts, doc, fragment,
8 i = 0,
9 l = this.length,
10 set = this,
11 iNoClone = l - 1,
12 value = args[0],
13 isFunction = jQuery.isFunction( value );
14
15 // We can't cloneNode fragments that contain checked, in WebKit
16 if ( isFunction ||
17 ( l > 1 && typeof value === "string" &&
18 !support.checkClone && rchecked.test( value ) ) ) {
19 return this.each(function( index ) {
20 var self = set.eq( index );
21 if ( isFunction ) {
22 args[0] = value.call( this, index, self.html() );
23 }
24 self.domManip( args, callback );
25 });
26 }
27
28 if ( l ) {
29 fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
30 first = fragment.firstChild;
31
32 if ( fragment.childNodes.length === 1 ) {
33 fragment = first;
34 }
35
36 if ( first ) {
37 scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
38 hasScripts = scripts.length;
39
40 // Use the original fragment for the last item instead of the first because it can end up
41 // being emptied incorrectly in certain situations (#8070).
42 for ( ; i < l; i++ ) {
43 node = fragment;
44
45 if ( i !== iNoClone ) {
46 node = jQuery.clone( node, true, true );
47
48 // Keep references to cloned scripts for later restoration
49 if ( hasScripts ) {
50 jQuery.merge( scripts, getAll( node, "script" ) );
51 }
52 }
53
54 callback.call( this[i], node, i );
55 }
56
57 if ( hasScripts ) {
58 doc = scripts[ scripts.length - 1 ].ownerDocument;
59
60 // Reenable scripts
61 jQuery.map( scripts, restoreScript );
62
63 // Evaluate executable scripts on first document insertion
64 for ( i = 0; i < hasScripts; i++ ) {
65 node = scripts[ i ];
66 if ( rscriptType.test( node.type || "" ) &&
67 !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
68
69 if ( node.src ) {
70 // Optional AJAX dependency, but won't run scripts if not present
71 if ( jQuery._evalUrl ) {
72 jQuery._evalUrl( node.src );
73 }
74 } else {
75 jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
76 }
77 }
78 }
79 }
80
81 // Fix #11809: Avoid leaking memory
82 fragment = first = null;
83 }
84 }
85
86 return this;
87 }
1.28行目-55行目、docmentFragmentを生成し、後続の解析実行のために
2.57行目-79行目、
3.69行目、
3.75行目、
1 globalEval: function( data ) {
2 if ( data && jQuery.trim( data ) ) {
3 // We use execScript on Internet Explorer
4 // We use an anonymous function so that context is window
5 // rather than jQuery in Firefox
6 ( window.execScript || function( data ) {
7 window[ "eval" ].call( window, data );
8 } )( data );
9 }
10 },