This section describes the replication element sub-module in the DOM operation module, which replicates a DOM node and optionally sets whether to replicate its data cache objects (including event information) and whether to replicate in depth (descendant nodes, etc.), with the following API s:
- $.clone(elem, dataAndEvents, deepDataAndEvents); jQuery underlying method, returning DOM reference; elem is the DOM element to be copied; dataAndEvents and deepDataAndEvents indicate whether to copy cloned element data and events, respectively; and whether to copy deep replication data and events.
- $.fn.clone(dataAndEvents,deepDataAndEvents); jQuery instance method, returns a jQuery object; parameter 2 equals parameter 1 if parameter 1 is specified and parameter 2 is empty
writer by:Desert QQ:22969969
Or raise a chestnut first:
<!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>The weather is nice today</p> </div> <button id="b1">Button 1</button> <button id="b2">Button 2</button> <button id="b3">Button 3</button> <button id="b4">Button 4</button> <script> $('div').click(function(){console.log('div click');}) //to div Bind one click Event $('p').click(function(){console.log('p click');}) //to p Bind one click Event /*Clicking on Button 1, Button 2, Button 3, Button 4 will copy the div and add it to the end of the body, then clicking on the p element in the copied block will output as follows:*/ $('#b1').click(function(){$('div').clone().appendTo('body');}) //No output $('#b2').click(function(){$('div').clone(true,false).appendTo('body');}) //output:clone_d1 click $('#b3').click(function(){$('div').clone(true,true).appendTo('body');}) //output:clone_p1 click,clone_d1 click $('#b4').click(function(){$('div').clone(true).appendTo('body');}) //output:clone_p1 click,clone_d1 click ;Because parameter 2 is omitted, it defaults to parameter 1 </script> </body> </html>
Render as follows:
We have two events bound on div and p, respectively. Click on the P element (the text for today's fine weather) console and output as follows:
In addition, any button we click will clone the div element, rendering as follows:
Simply click on the p element of the cloned element, that is, the text the arrow clicks, and the console output will be different, for the four buttons, as follows:
- Button 1; No Output > clone() does not pass in parameters, so data cache objects will not be copied
- Button 2; Output: One line: div click; clone() passes in true and false, so only one copy of the div's data cache object will be made, not p
- Button 3; Output two lines: p click and div click; clone() passes in two true, making a deep copy, and the data objects of the descendant nodes are copied over
- Button 4; Output two lines: p click and div click; Clone () passes in only one true, the same as Button 3, and see the code analysis below
Source Code Analysis
Start with $.clone(), which is jQuery's underlying method, as follows:
jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) { //copy DOM Element, and fix incompatible attributes. dataAndEvents:Whether to copy data and events. deepDataAndEvents:Whether to copy data and events of descendant elements. var srcElements, destElements, i, // IE<=8 does not properly clone detached, unknown element nodes clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" + elem.nodeName ) ? //If browser supports HTML5 Element, or not supported html5 And the original element does not contain html5 element elem.cloneNode( true ) : //Call Native Method.clone(deep)copy DOM element shimCloneNode( elem ); //Otherwise, call the function shimCloneNode()adopt"Secure Document Fragments"copy HTML5 element if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && //Correct Incompatibility (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 ) { //If data and events are copied cloneCopyEvent( elem, clone ); //call cloneCopyEvent()data if ( deepDataAndEvents ) { //If it is deep copy srcElements = getAll( elem ); //Get Source DOM Node All Child Nodes DOM Quote destElements = getAll( clone ); //Get Goals DOM Node All Child Nodes DOM Quote for ( i = 0; srcElements[i]; ++i ) { //Traversing Sources DOM Child nodes of a node cloneCopyEvent( srcElements[i], destElements[i] ); //Copy data caches one by one } } } srcElements = destElements = null; // Return the cloned set return clone; //Last Return clone,That is, copied out DOM element }, })
$.clone() calls the native cloneNode() method to copy a DOM, and cloneCopyEvent() copies the data cache as follows:
function cloneCopyEvent( src, dest ) { //Copy data and events ;$.clone()function call ;src Is Source DOM Node, dest Is Target Node if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { //If dest Not an element node or src Return without data return; } var type, i, l, oldData = jQuery._data( src ), //Get the internal data of the original object curData = jQuery._data( dest, oldData ), //Set the internal data of the target object ;At this time src and dest Event objects within two objects(events and handle attribute)All point to the same events = oldData.events; //Event object of original object if ( events ) { //If Source DOM If the object has a bound event handler, delete the target DOM Event information for the object, then rebind delete curData.handle; //Delete the listening handle of the target object curData.events = {}; //Reset the list of events for the target object for ( type in events ) { //Traversing Sources DOM Event List of Objects for ( i = 0, l = events[ type ].length; i < l; i++ ) { //Traverse through each of the bound functions jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data ); //Target in turn DOM Bind event operations on objects } } } // make the cloned public data object a copy from the original if ( curData.data ) { //If Source DOM Object Contains Custom Event Object curData.data = jQuery.extend( {}, curData.data ); //Also make a separate copy and save it to curData.data in } }
As you can see from the source code, if an event object is bound, jQuery.event.add() is called to bind in turn, which is what happens.
For the instance method $.fn.clone(), its implementation is as follows:
jQuery.fn.extend({ clone: function( dataAndEvents, deepDataAndEvents ) { //Create a deep copy of the set of matching elements. dataAndEvents:Optional Boolean value indicating whether to copy data and events, defaulting to false. deepDataAndEvents:An optional Boolean value indicating whether to copy data and events in depth, defaulting to false. dataAndEvents = dataAndEvents == null ? false : dataAndEvents; //correct dataAndEvents parameter deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; //correct deepDataAndEvents parameter return this.map( function () { //Call that hair.map()Traverse through the set of matching elements, called in callback function jQuery.clone()Copy each matching element. return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); //Call underlying jQuery.clone()Method }); }, })
As you can see here, for $.fn.clone(), if parameter 2 is not passed, then parameter 1 will be corrected, and button 4 in the example above is executed here.