クローンノードはいつ使用されますか?
DOM 操作中にノードが直接使用される場合、操作に応じてノードが変更されることがわかっています。たとえば、ノード上で .after/.before/.append およびその他のメソッドを使用した後、ノードは新しい場所に追加され、元の場所にあったノードは削除されます。場合によっては、ノードを元の場所に保持し、対応する場所にコピーを追加するだけで済む場合があります。この場合、クローン作成にはユースケースがあります。
jQuery.fn.clone は、一致する要素の現在のセットのコピーを複製し、それを jQuery オブジェクトとして返します。
追加のデータ (data() 関数) と、これらの一致する要素 (その子も含む) のバインドされたイベントをコピーするかどうかを指定することもできます。
jQueyr.fn.clone: function(withDataAndEvents, deepDataAndEvents) パラメーターの説明
a. クローン関数の基本的な実装手順は次のように分類されます (jQuery.clone)
最初のステップは、DOM ノードのクローンを作成することです。正しいノード クローン作成をサポートする (つまり、elem.cloneNode をサポートし、正しいクローン作成を保証する) DOM ノードに対して cloneNode(true) を直接使用します。それ以外の場合は、ノードを構築してクローン データを保存してからノードを取得します。
if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { clone = elem.cloneNode( true ); // IE<=8 不能正确克隆已分离、未知的节点 //直接新建一个相同的节点,然后获取 } else { //fragmentDiv是全局变量 fragmentDiv.innerHTML = elem.outerHTML; fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); }
2 番目のステップでは、IE ブラウザを使用している場合は、fixCloneNodeIssues(node, destElements[i]); を通じて IE のクローン作成の問題を 1 つずつ修正する必要があります。すべての IE クローン作成ソリューションは fixCloneNodeIssues に含まれており、次のセクションで詳しく分析します。 jQuery.support コンテンツをさらに表示するには、ここをクリックしてください
//针对ie克隆问题修正 if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { //在这里我们不使用Sizzle的原因是: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); //修正所有IE克隆问题 for ( i = 0; (node = srcElements[i]) != null; ++i ) { // Ensure that the destination node is not null; Fixes #9587 if ( destElements[i] ) { fixCloneNodeIssues( node, destElements[i] ); } } }
3 番目のステップでは、キャッシュ データ (通常のデータとバインディング イベントを含む) のクローンを作成する場合、それをクローンします。
//克隆绑定的事件 if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0; (node = srcElements[i]) != null; i++ ) { cloneCopyEvent( node, destElements[i] ); } } else { cloneCopyEvent( elem, clone ); } }
注: cloneCopyEvent 関数は、元のノードのデータをクローン ノードに保存し、元のノードのイベントを新しいクローン ノードにバインドします
function cloneCopyEvent( src, dest ) { if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { return; } var type, i, l, oldData = jQuery._data( src ), curData = jQuery._data( dest, oldData ),//dest是克隆对的节点 events = oldData.events; if ( events ) { //保证被克隆的节点的事件对象干净,确保没有后面添加的事件没有重复 delete curData.handle; curData.events = {}; for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } // 使克隆的数据对象化 if ( curData.data ) { curData.data = jQuery.extend( {}, curData.data ); } }
4 番目のステップは、スクリプト計算履歴を保護し (スクリプト コード セグメントを実行済みとしてグローバルにマークする)、メモリを再利用し、クローン ノードに戻ることです。
destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } destElements = srcElements = node = null; return clone;
b. IE クローン作成の問題の概要 fixCloneNodeIssues(src,dest)
src は元のノード、dest は src のクローン ノードです。
IE クローン作成の問題をリストします (IE8)
1.IE6-8 は、cloneNode を使用するときにイベントを複製します (これらのイベントは、attachEvent を通じてバインドされます)。均一性を確保するには、クローン イベントをクリアし、後続の統合クローン イベントに備える必要があります
// IE6-8当使用cloneNode复制事件(这些事件绑定通过attachEvent)时进入该分支 //清除原来的事件,为克隆事件做准备 if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { data = jQuery._data( dest ); for ( e in data.events ) { jQuery.removeEvent( dest, e, data.handle ); } dest.removeAttribute( jQuery.expando ); }
2.IE8 - scriptタグscriptを複製すると、複製された内容が空白になります。これを再割り当てし、スクリプトの内容が実行されないようにする必要があります。
//IE克隆脚本时内容为空白,并试图执行新设置的文本 if ( nodeName === "script" && dest.text !== src.text ) { disableScript( dest ).text = src.text; restoreScript( dest ); }
3.IE6-10ではclassidを使用して取得したobject要素の子ノードを複製できません。 IE10 では、親ノードが null の場合、NoModificationAllowedError 例外がスローされます。元のノードのouterHTMLとinnerHTMLを再割り当てする必要があります。
//IE6-10不能克隆使用的classid获取的对象元素的子节点。 //IE10下,如果父节点为null,则会抛出NoModificationAllowedError异常 else if ( nodeName === "object" ) { if ( dest.parentNode ) { dest.outerHTML = src.outerHTML; } //对于IE9,这个条分支不可避免。 //IE9中克隆对象元素,上述outerHTML策略是不充分的。 //如果src具有的innerHTML并且克隆节点却没有, //复制src.innerHTML到dest.innerHTML #10324 if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { dest.innerHTML = src.innerHTML; } }
4.IE6-8ではチェックボックスやラジオボタンの選択状態を複製できません。アクティブな設定が必要です。
// manipulation_rcheckableType = /^(?:checkbox|radio)$/i else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { //IE6-8无法坚持一个克隆的复选框或单选按钮的选中状态 //更糟的是,如果defaultChecked值没有设置,则IE6-7无法给克隆元素选中状态的外观 dest.defaultChecked = dest.checked = src.checked; ... }
5. select タグを複製すると、IE6-8 は select のデフォルトの選択状態に正しく戻ることができません。アクティブな設定が必要です。
//当克隆选项时,IE6-8无法正确返回select默认选中状态 else if ( nodeName === "option" ) { dest.defaultSelected = dest.selected = src.defaultSelected; }
6. 他のタイプの input タグと textare タグを複製する場合、IE6 ~ 8 では、defaultValue を正しい値に正しく設定できません。アクティブな設定が必要です。
//当克隆其他类型的input标签时,IE6-8不能正确设置defaultValue为正确的值 else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; }
内部ではdisableScript関数が使用されています。この関数の目的は、スクリプトに値を割り当てた後にスクリプトが実行されないように、スクリプトのタイプを変更することです。このアプローチから学ぶことができます
//为安全DOM操作替换/保存script节点元素type属性 function disableScript( elem ) { var attr = elem.getAttributeNode("type"); elem.type = ( attr && attr.specified ) + "/" + elem.type; return elem; }
上記の内容は、エディターが紹介したjQuery-1.9.1ソースコード解析シリーズ(11)クローニングノードのDOM操作の続きの全解説となります。