jQuery 3.0的buildFragment

在 jQuery3.0中,buildFragment 是一個私有函數,用來構建一個包含子節點 fragment 對象。這個 fragment 在 DOM1 中就已經有了,全部瀏覽器都支持。當頻繁操做(添加、插入) DOM 時使用該方法能夠提升性能,John resig 作過一個測試及一篇博客javascript

 

jQuery3.0 中 buildFragment 只在 domManip 和 jQuery.parseHTML 中使用,domManip 則被 DOM 操做如 append、prepend、before、after 等方法的所依賴。以下圖html

 

buildFragment 函數有 5 個參數,源碼以下java

function buildFragment( elems, context, scripts, selection, ignored ) {
	var elem, tmp, tag, wrap, contains, j,
		fragment = context.createDocumentFragment(),
		nodes = [],
		i = 0,
		l = elems.length;

	for ( ; i < l; i++ ) {
		elem = elems[ i ];

		if ( elem || elem === 0 ) {

			// Add nodes directly
			if ( jQuery.type( elem ) === "object" ) {

				// Support: Android <=4.0 only, PhantomJS 1 only
				// push.apply(_, arraylike) throws on ancient WebKit
				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );

			// Convert non-html into a text node
			} else if ( !rhtml.test( elem ) ) {
				nodes.push( context.createTextNode( elem ) );

			// Convert html into DOM nodes
			} else {
				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );

				// Deserialize a standard representation
				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
				wrap = wrapMap[ tag ] || wrapMap._default;
				tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];

				// Descend through wrappers to the right content
				j = wrap[ 0 ];
				while ( j-- ) {
					tmp = tmp.lastChild;
				}

				// Support: Android <=4.0 only, PhantomJS 1 only
				// push.apply(_, arraylike) throws on ancient WebKit
				jQuery.merge( nodes, tmp.childNodes );

				// Remember the top-level container
				tmp = fragment.firstChild;

				// Ensure the created nodes are orphaned (#12392)
				tmp.textContent = "";
			}
		}
	}

	// Remove wrapper from fragment
	fragment.textContent = "";

	i = 0;
	while ( ( elem = nodes[ i++ ] ) ) {

		// Skip elements already in the context collection (trac-4087)
		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
			if ( ignored ) {
				ignored.push( elem );
			}
			continue;
		}

		contains = jQuery.contains( elem.ownerDocument, elem );

		// Append to fragment
		tmp = getAll( fragment.appendChild( elem ), "script" );

		// Preserve script evaluation history
		if ( contains ) {
			setGlobalEval( tmp );
		}

		// Capture executables
		if ( scripts ) {
			j = 0;
			while ( ( elem = tmp[ j++ ] ) ) {
				if ( rscriptType.test( elem.type || "" ) ) {
					scripts.push( elem );
				}
			}
		}
	}

	return fragment;
}

 

該方法主要執行步驟node

  1. 經過第二個參數 content 建立 fragment
  2. 經過第一個參數 elems 構建 nodes ,將 elems 內元素轉成 DOM 元素存放於數組 nodes 中
  3. 將 nodes 裏元素循環放入添加到文檔碎片 fragment 上
  4. 返回 fragment

 

重點在第 2 步,構建 nodes,有 3 種情形程序員

  1. elem 是 DOM 元素(根據nodeType判斷),直接放入 nodes 數組中
  2. elem 是字符串且不是 HTML tag,建立文本節點對象(textNode),放入 nodes 數組中
  3. elem 是字符串且是 HTML tag,將其轉成 DOM 元素,放入 nodes 數組中

 

如圖示數組

 

後面的兩個參數須要注意下瀏覽器

1. 最後兩個參數 selection 和 ignored 只在 replaceWith 方法裏使用。須要瞭解的是 replaceWith 只作節點替換,不會替換先前元素的全部數據(Data),好比綁定事件,$.data 都不會被新元素擁有。app

 

2. scripts 參數只在 jQuery.parseHTML 方法裏使用(domManip裏傳false),當 jQuery.parseHTML 的第三個參數 keepScripts 爲 false 時將刪除節點裏全部的 script tagdom

 

buildFragment 在 jQuery 各個版本中的演變函數

  1. 1.0.x ~ 1.3.x 中沒有 buildFragment 函數,即沒有抽取出該函數爲 domManip 服務
  2. 1.4.x 中首次引入 buildFragment ,當時是掛在 jQuery 上的靜態方法,有三個參數 args, nodes, scripts。一直到2.x.x 依然是公開能夠訪問的
  3. 3.x.x 開始 buildFragment 變成了一個私有函數,只能在 jQuery 代碼內部訪問,客戶端程序員沒法訪問
相關文章
相關標籤/搜索