Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #12956 - Improve cloneFixAttributes function #1034

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 15 additions & 26 deletions src/manipulation.js
Expand Up @@ -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 ) {
Expand Down Expand Up @@ -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 ) {
Expand Down Expand Up @@ -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] ) {
Expand All @@ -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] );
Expand Down
49 changes: 49 additions & 0 deletions test/unit/event.js
Expand Up @@ -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
});