jQuery Source Parsing DOM Operation Module Replication Element Details

Keywords: Javascript JQuery html5 IE Attribute

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.

Posted by Vince on Sun, 10 Nov 2019 17:42:17 -0800