jQuery 源碼解析(二十二) DOM操做模塊 複製元素 詳解

本節說一下DOM操做模塊裏的複製元素子模塊,該模塊能夠複製一個DOM節點,而且可選擇的設置是否複製其數據緩存對象(包含事件信息)和是否深度複製(子孫節點等),API以下:html

  • $.clone(elem, dataAndEvents, deepDataAndEvents)    ;jQuery底層方法,返回DOM引用    ;elem是要複製的DOM元素,dataAndEvents和deepDataAndEvents分別表示是否複製克隆元素的數據和事件 和 是否複製深度複製數據和事件 
  • $.fn.clone(dataAndEvents,deepDataAndEvents)        ;jQuery實例方法,返回jQuery對象    ;參數同上,若是指定了參數1,參數2爲空時,則參數2等於參數1 

 writer by:大沙漠 QQ:22969969html5

仍是先舉個栗子:node

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <div style="width: 150px;height: 50px;background: #cfc;">
        <p>今每天氣很好</p>
    </div>
    <button id="b1">按鈕1</button>
    <button id="b2">按鈕2</button>
    <button id="b3">按鈕3</button>
    <button id="b4">按鈕4</button>

    <script> $('div').click(function(){console.log('div click');}) //給div綁定一個click事件
 $('p').click(function(){console.log('p click');}) //給p綁定一個click事件

        /*點擊按鈕一、按鈕二、按鈕三、按鈕4都將會複製div,並添加到body的末尾,再點擊複製出來的區塊裏的p元素時輸出以下:*/ $('#b1').click(function(){$('div').clone().appendTo('body');}) //沒有任何輸出
 $('#b2').click(function(){$('div').clone(true,false).appendTo('body');}) //輸出:clone_d1 click
 $('#b3').click(function(){$('div').clone(true,true).appendTo('body');}) //輸出:clone_p1 click、clone_d1 click
 $('#b4').click(function(){$('div').clone(true).appendTo('body');}) //輸出:clone_p1 click、clone_d1 click ;由於參數2省略,默認等於參數1
    </script>
</body>
</html>

渲染以下:jquery

咱們在div和p上分別綁定了兩個事件,點擊p元素(今每天氣很好這個文本)控制檯輸出以下:瀏覽器

 另外咱們點擊任意一個按鈕都會克隆div元素,渲染以下:緩存

只不過點擊克隆出來的元素的p元素,也就是箭頭點擊的文字,控制檯輸出的內容不一樣,對於四個按鈕分別以下:安全

  • 按鈕1 ;無輸出                                 ;clone()未傳入參數,所以不會複製數據緩存對象
  • 按鈕2 ;輸出:一行:div click               ;clone()傳入了true和false,所以只會對div的數據緩存對象作一次拷貝,對於p就不會拷貝了
  • 按鈕3 ;輸出兩行:p click和div click  ;clone()傳入了兩個true,會進行深層次的拷貝,子孫節點的數據對象都會拷貝過來
  • 按鈕4  ;輸出兩行:p click和div click ;clone()只傳入一個true,這和按鈕3是同樣的,且看下面代碼分析

 

源碼分析app


 先介紹一下$.clone(),也就是jQuery的底層方法,以下:函數

jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) {         //複製DOM元素,並修正不兼容屬性。dataAndEvents:是否複製數據和事件。deepDataAndEvents:是否複製後代元素的數據和事件。
        var srcElements, destElements, i, // IE<=8 does not properly clone detached, unknown element nodes
            clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ?    //若是瀏覽器支持HTML5元素,或者不支持html5且原始元素不含有html5元素 
                elem.cloneNode( true ) :                                                            //調用原生方法.clone(deep)複製DOM元素 
                shimCloneNode( elem );                                                              //不然調用函數shimCloneNode()經過"安全文檔片斷"複製HTML5元素

        if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&                //修正不兼容性
                (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { // IE copies events bound via attachEvent when using cloneNode.
            // Calling detachEvent on the clone will also remove the events
            // from the original. In order to get around this, we use some
            // proprietary methods to clear the events. Thanks to MooTools
            // guys for this hotness.
 cloneFixAttributes( elem, clone ); // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
            srcElements = getAll( elem ); destElements = getAll( clone ); // Weird iteration because IE will replace the length property
            // with an element if you are cloning the body and one of the
            // elements on the page has a name or id of "length"
            for ( i = 0; srcElements[i]; ++i ) { // Ensure that the destination node is not null; Fixes #9587
                if ( destElements[i] ) { cloneFixAttributes( srcElements[i], destElements[i] ); } } } // Copy the events from the original to the clone
        if ( dataAndEvents ) {                      //若是複製數據和事件
            cloneCopyEvent( elem, clone );              //調用cloneCopyEvent()數據

            if ( deepDataAndEvents ) {                  //若是是深度複製
                srcElements = getAll( elem );               //獲取源DOM節點全部子節點的DOM引用
                destElements = getAll( clone );             //獲取目標DOM節點全部子節點的DOM引用

                for ( i = 0; srcElements[i]; ++i ) {        //遍歷源DOM節點的子節點
                    cloneCopyEvent( srcElements[i], destElements[i] );  //逐個複製數據緩存
 } } } srcElements = destElements = null; // Return the cloned set
        return clone;                               //最後返回clone,也就是複製出來的DOM元素
 }, })

$.clone()調用原生的cloneNode()方法拷貝了一份DOM,對於數據緩存則用cloneCopyEvent()進行拷貝,該函數實現以下:源碼分析

function cloneCopyEvent( src, dest ) {      //複製數據和事件 ;$.clone()函數調用 ;src是源DOM節點,dest是目標節點

    if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {  //若是dest不是元素節點或 src不含有數據,則直接返回
        return; } var type, i, l, oldData = jQuery._data( src ),                      //獲取原對象內部數據
        curData = jQuery._data( dest, oldData ),            //設置目標對象的內部數據 ;這時src和dest兩個對象內的事件對象(events和handle屬性)都是指向同一個的
        events = oldData.events;                            //原對象的事件對象

    if ( events ) {                                         //若是源DOM對象有綁定事件處理函數,則刪除目標DOM對象的事件信息,再從新綁定
        delete curData.handle;                                  //刪除目標對象的監聽句柄
        curData.events = {};                                    //重置目標對象的事件列表

        for ( type in events ) {                                //遍歷源DOM對象的事件列表
            for ( i = 0, l = events[ type ].length; i < l; i++ ) {  //遍歷綁定的每一個函數
                jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );   //依次在目標DOM對象上進行綁定事件操做
 } } } // make the cloned public data object a copy from the original
    if ( curData.data ) {                                   //若是源DOM對象含有自定義事件對象
        curData.data = jQuery.extend( {}, curData.data );       //也單獨拷貝一份,再保存到curData.data中
 } }

在源碼裏能夠看到,若是有綁定了事件對象則會調用jQuery.event.add()依次進行綁定,實現就是這樣子的。

對於實例方法$.fn.clone()來講,它的實現以下:

jQuery.fn.extend({ clone: function( dataAndEvents, deepDataAndEvents ) {           //建立匹配元素集合的深度複製副本。dataAndEvents:可選的布爾值,表示是否複製數據和事件,默認爲false。deepDataAndEvents:可選的布爾值,表示是否深度複製數據和事件,默認爲false。
        dataAndEvents = dataAndEvents == null ? false : dataAndEvents;                      //修正dataAndEvents參數
        deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;  //修正deepDataAndEvents參數 
        return this.map( function () {                                      //調用發那個發.map()遍歷匹配元素集合,在回調函數函數中調用jQuery.clone()複製每一個匹配元素。
            return jQuery.clone( this, dataAndEvents, deepDataAndEvents );      //調用底層的jQuery.clone()方法
 }); }, })

這裏能夠看到,對於$.fn.clone()來講,若是參數2沒有傳遞,則會修正未參數1,上面例子的按鈕4就是執行到這裏的。

相關文章
相關標籤/搜索