首頁 >web前端 >js教程 >jQuery-1.9.1源碼分析系列(十一)DOM操作續之克隆節點_jquery

jQuery-1.9.1源碼分析系列(十一)DOM操作續之克隆節點_jquery

WBOY
WBOY原創
2016-05-16 15:28:321312瀏覽

什麼情況下使用到複製節點?

  我們知道在對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 );
}

  第二步,如果是IE瀏覽器下,則需要透過fixCloneNodeIssues( node, destElements[i] );來逐個修正IE複製問題。 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] );
    }
  }
}

   第三步,如果要複製快取資料(包含普通資料和綁定事件),複製之。

//克隆绑定的事件
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 );
    }
  }

  第四步,保護script計算歷史(全局性地標記scripts程式碼段已經被執行過了),並回收內存,返回克隆節點。

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的時候克隆的內容結果會是空白。我們需要為他重新賦值,並確保他不會執行腳本內容。

//IE克隆脚本时内容为空白,并试图执行新设置的文本
  if ( nodeName === "script" && dest.text !== src.text ) {
    disableScript( dest ).text = src.text;
    restoreScript( dest );
    }

  3.IE6-10不能複製使用的classid所取得的物件元素的子節點。 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 = /^(&#63;: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這個函數。函數目的是改變script的type,從而確保在給script賦值後不會被當作腳本執行。這個方式我們可以參考

//为安全DOM操作替换/保存script节点元素type属性
function disableScript( elem ) {
  var attr = elem.getAttributeNode("type");
  elem.type = ( attr && attr.specified ) + "/" + elem.type;
  return elem;
}

以上內容是小編給大家介紹的關於jQuery-1.9.1源碼分析系列(十一)DOM操作續之克隆節點的全部敘述,希望大家喜歡。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn