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

来源:https://www.cnblogs.com/greatdesert/archive/2019/11/11/11814242.html
-Advertisement-
Play Games

本節說一下DOM操作模塊里的複製元素子模塊,該模塊可以複製一個DOM節點,並且可選擇的設置是否複製其數據緩存對象(包含事件信息)和是否深度複製(子孫節點等),API如下: $.clone(elem, dataAndEvents, deepDataAndEvents) ;jQuery底層方法,返回DO ...


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

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

 writer by:大沙漠 QQ:22969969

還是先舉個慄子:

<!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事件

        /*點擊按鈕1、按鈕2、按鈕3、按鈕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>

渲染如下:

我們在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是一樣的,且看下麵代碼分析

 

源碼分析


 先介紹一下$.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就是執行到這裡的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 國瑞前端: js確定取消,在html界面中,有css模擬的模態框,這樣顯示的就會更好看一些,那麼javascript有沒有自帶的彈框呢,當然是有的,接下來我就來給大家介紹一下把: js確定取消-警告框alert() 起到警告作用 js確定取消-確認框confirm() 123456789101112 ...
  • 響應式佈局 一個網站能夠相容多個終端,並且在各個終端都可以很好展示體驗。 媒體類型 在何種設備或者軟體上將頁面打開 123456789 all:所有媒體braille:盲文觸覺設備embossed:盲文印表機print:手持設備projection:印表機預覽screen:彩屏設備speech:'聽 ...
  • 小程式由來 當初結婚,為了給媳婦驚喜,藉助雲開發的推廣,自己做了一個結婚用的邀請函小程式,結完婚後,趁著空閑時間,希望把之前使用的邀請函小程式留存下來,所以做了一次整體改版。改版後的小程式我自己定義為相冊類小程式,主要放一些自己喜歡的照片。 小程式功能 首頁相冊 愛的空間相冊 陌生人的到訪 陌生人的 ...
  • 中間件是什麼呢? 中間件就是客戶端http請求發起傳送到伺服器和伺服器返迴響應之間的一些處理函數。 為什麼要使用中間件? 通過中間件,可以對數據進行操作使得我們能方便地操作請求數據編寫伺服器響應。如body-parse中間件對post請求的參數進行處理讓我們可以通過res.body快速獲取請求參數, ...
  • 一、項目簡介 uni-liveShow是一個基於vue+uni-app技術開發的集小視頻/IM聊天/直播等功能於一體的微直播項目。界面仿製抖音|火山小視頻/陌陌直播,支持編譯到多端(H5、小程式、App端) 且相容效果一致。 二、效果預覽 在H5、小程式、App端測試效果如下:(後續大圖均為APP端 ...
  • 最近在工作中遇到了一個百度地圖api中的難題,恐怕有的程式員可能也遇到過。就是實時定位並顯示軌跡,網上大部分都是通過創建polyline對象貼到地圖上。當然,百度地圖的畫線就是這樣實現的,但是好多人會發現,如果數據量超過四五千的話,瀏覽器可能就要卡了,後面可能就直接因為瀏覽器記憶體不足直接崩潰。按理講 ...
  • 文字摺疊效果 1. 效果展示 2. 實現過程 ~~~html H e l l o W o r l d ~~~ 填充內容 CSS佈局 ~~~css { margin:0px; padding:0px; } body{ background color:aquamarine; width:100%; h ...
  • 1.什麼是window? window:是一個全局對象, 代表瀏覽器中一個打開的視窗, 每個視窗都是一個window對象2.什麼是document? document是window的一個屬性, 這個屬性是一個對象 document: 代表當前視窗中的整個網頁, document對象保存了網頁上所有的 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...