diff --git a/src/manipulation.js b/src/manipulation.js index 6521e885ee..5180acab82 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -444,26 +444,26 @@ function cloneCopyEvent( src, dest ) { } function cloneFixAttributes( src, dest ) { - var nodeName; + var nodeName, data, e; // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { return; } - // clearAttributes removes the attributes, which we don't want, - // but also removes the attachEvent events, which we *do* want - if ( dest.clearAttributes ) { - dest.clearAttributes(); - } + nodeName = dest.nodeName.toLowerCase(); - // mergeAttributes, in contrast, only merges back on the - // original attributes, not the events - if ( dest.mergeAttributes ) { - dest.mergeAttributes( src ); - } + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); - nodeName = dest.nodeName.toLowerCase(); + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } // IE blanks contents when cloning scripts, and tries to evaluate newly-set text if ( nodeName === "script" && dest.text !== src.text ) { @@ -508,10 +508,6 @@ function cloneFixAttributes( src, dest ) { } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } - - // Event data gets referenced instead of copied if the expando - // gets copied too - dest.removeAttribute( jQuery.expando ); } jQuery.buildFragment = function( args, context, scripts ) { @@ -636,19 +632,12 @@ jQuery.extend({ 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. // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); - // 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" + // Fix all IE cloning issues for ( i = 0; (node = srcElements[i]) != null; ++i ) { // Ensure that the destination node is not null; Fixes #9587 if ( destElements[i] ) { @@ -660,8 +649,8 @@ jQuery.extend({ // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { - destElements = getAll( clone ); - srcElements = getAll( elem ); + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); for ( i = 0; (node = srcElements[i]) != null; i++ ) { cloneCopyEvent( node, destElements[i] ); diff --git a/test/unit/event.js b/test/unit/event.js index 78f7fe9afc..b56b45ef2f 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -3104,4 +3104,53 @@ test( "Namespace preserved when passed an Event (#12739)", function() { equal( triggered, 3, "foo.bar triggered" ); }); +test( "make sure events cloned correctly", 18, function() { + var clone, + fixture = jQuery("#qunit-fixture"), + checkbox = jQuery("#check1"), + p = jQuery("#firstp"); + fixture.on( "click change", function( event, result ) { + ok( result, event.type + " on original element is fired" ); + + }).on( "click", "#firstp", function( event, result ) { + ok( result, "Click on original child element though delegation is fired" ); + + }).on( "change", "#check1", function( event, result ) { + ok( result, "Change on original child element though delegation is fired" ); + }); + + p.on("click", function( event, result ) { + ok( true, "Click on original child element is fired" ); + }); + + checkbox.on("change", function( event, result ) { + ok( true, "Change on original child element is fired" ); + }); + + fixture.clone().click().change(); // 0 events should be fired + + clone = fixture.clone( true ); + + clone.find("p:first").trigger( "click", true ); // 3 events should fire + clone.find("#check1").trigger( "change", true ); // 3 events should fire + clone.remove(); + + clone = fixture.clone( true, true ); + clone.find("p:first").trigger( "click", true ); // 3 events should fire + clone.find("#check1").trigger( "change", true ); // 3 events should fire + + fixture.off(); + p.off(); + checkbox.off(); + + p.click(); // 0 should be fired + checkbox.change(); // 0 should be fired + + clone.find("p:first").trigger( "click", true ); // 3 events should fire + clone.find("#check1").trigger( "change", true ); // 3 events should fire + clone.remove(); + + clone.find("p:first").click(); // 0 should be fired + clone.find("#check1").change(); // 0 events should fire +});