Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Fixes #12956 - Improve cloneFixAttributes function #1034

Closed
wants to merge 2 commits into from

4 participants

Oleg Gaidarenko Dave Methvin Rick Waldron Mike Sherov
Oleg Gaidarenko
Collaborator

Events bounded via addEventListener will not be cloned in IE, events bounded via attachEvent will, but in IE9-10, jQuery does not use attachEvent method and even if it did, clear(merge)Attributes hack would not work for those events anyway.

So for new IE, there is no reason to use this hack.

Expando property is not cloned in IE9-10 and it might not exist at all, but removeAttribute method is expensive to use, it would be better to check for existence of expando before trying to remove it.

I would perform clear(merge)Attributes hack only if expando property is exist, but it would affect user events bounded via attachEvent, i'm wondering - is it a real issue?

/cc @dmethvin

Dave Methvin
Owner

This makes sense too. We only want to use the clearAttributes/mergeAttributes junk on oldIE.

Oleg Gaidarenko
Collaborator

@dmethvin

We only want to use the clearAttributes/mergeAttributes junk on oldIE

Do you?

From where i'm standing this is not true. If so, and this –

but it would affect user events bounded via attachEvent, i'm wondering - is it a real issue?

is not a problem, you can eliminate clear(merge)Attributes hack altogether, by using detachEvent,
which will speed up cloning process and solve #9646.

Dave Methvin
Owner

For IE9 and higher, we use addEventListener for our own needs, so we don't need it there. It's possible that someone is using attachEvent on their own elements in IE and then cloning the element, is that the case you were concerned about? That is really undefined territory. Anything attached directly with either attachEvent or addEventListener is basically something the user would need to worry about.

Rick Waldron
Collaborator

Two things... First, is there a ticket for this? Second, the patch needs to be rebased—Thanks! :)

Oleg Gaidarenko
Collaborator

@rwldrn

First

No, do you need one?

Second

Wait a bit, i will rebase shortly

Rick Waldron
Collaborator

No, do you need one?

We've been over this, yes, please make tickets for bugs/features/changes... everything

Oleg Gaidarenko
Collaborator
We've been over this, yes, please make tickets for bugs/features/changes... everything

Yeah, we did, many times, but i ask because your policy often changes, and sometimes, you push commits without tickets... So, as i understand – de jure i have to, but not de facto.

Oleg Gaidarenko
Collaborator
Rick Waldron
Collaborator

If you're referring to 4fed8eb, @krinkle and I discussed this in person and the change itself is trivial.

This still needs to be rebased.

Oleg Gaidarenko
Collaborator
If you're referring to 4fed8eb, @krinkle and I discussed this in person and the change itself is trivial.

By "you" i did not mean you specifically, i mean team does this – 1, 2, 3, 4...

This still needs to be rebased.

Before i do, i want to try couple of things with this code.

Rick Waldron
Collaborator
  1. Doesn't change any jQuery source
  2. Doesn't change any jQuery source
  3. @dmethvin is the leader of the project and has more understanding of the entire code base then anyone else in the world.
  4. I agree that this should've had a ticket.
Oleg Gaidarenko
Collaborator
Doesn't change any jQuery source

So?

@dmethvin is the leader of the project and has more understanding of the entire code base then anyone else in the world.

And?

These examples are not the only one, but never mind, i can create tickets if you like.

Oleg Gaidarenko
Collaborator

@rwldrn Although, if issue is purely with code it would be nice if it was discussed only at github, it's tediously to converse in different places

Rick Waldron
Collaborator

"Github Issues" is inadequate for our needs. I'm not interested in arguing about this any further.

Dave Methvin
Owner

I'm not sure what is being argued, but @orkel has been working on some of these tickets for a while. They just got tangled up with some other pull requests in the same vicinity.

Oleg Gaidarenko
Collaborator
This still needs to be rebased.

done.

It's possible that someone is using attachEvent on their own elements in IE and then cloning the element, is that the case you were concerned about?

Yep.

Anything attached directly with either attachEvent or addEventListener is basically something the user would need to worry about.

OK, then let's try new approach, i added more tests, just in case.

Dave Methvin

We are just in IE 6-8 territory here now, right? Can we just use dest.detachEvent( "on"+e, data.handle ) directly?

Collaborator

We can, but jQuery.removeEvent also deals with oldIE memory leaks.

Dave Methvin

That's a mighty bold statement!

Collaborator

That's just how I roll!
I can rephrase it to "almost every", just like here.

Dave Methvin
Owner

So what should we do with gh-1036? Do you want to rebase that after this lands?

Oleg Gaidarenko
Collaborator

@dmethvin

Do you want to rebase that after this lands?

or vice-versa, whatever you decide...

Mike Sherov
Collaborator

@jquerybot retest

Erik James Albaugh leoken referenced this pull request from a commit in leoken/jquery August 06, 2012
Corey Frang Fixes #1034 - Check for style.removeAttribute before calling it
Fixes issue in non IE browsers that happen to come down this path
07e5093
Dave Methvin dmethvin closed this pull request from a commit November 18, 2012
Oleg Gaidarenko Fix #12956. Improve cloneFixAttributes for IE9/10 case. Close gh-1034.
Remove clear(merge)Attributes hack
93e1892
Dave Methvin dmethvin closed this in 93e1892 December 11, 2012
tp9 referenced this pull request from a commit January 02, 2013
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Nov 27, 2012
Oleg Gaidarenko Improve cloneFixAttributes function a318fa9
Nov 28, 2012
Oleg Gaidarenko Remove clear(merge)Attributes hack 6d0804d
This page is out of date. Refresh to see the latest.
41  src/manipulation.js
@@ -444,26 +444,26 @@ function cloneCopyEvent( src, dest ) {
444 444
 }
445 445
 
446 446
 function cloneFixAttributes( src, dest ) {
447  
-	var nodeName;
  447
+	var nodeName, data, e;
448 448
 
449 449
 	// We do not need to do anything for non-Elements
450 450
 	if ( dest.nodeType !== 1 ) {
451 451
 		return;
452 452
 	}
453 453
 
454  
-	// clearAttributes removes the attributes, which we don't want,
455  
-	// but also removes the attachEvent events, which we *do* want
456  
-	if ( dest.clearAttributes ) {
457  
-		dest.clearAttributes();
458  
-	}
  454
+	nodeName = dest.nodeName.toLowerCase();
459 455
 
460  
-	// mergeAttributes, in contrast, only merges back on the
461  
-	// original attributes, not the events
462  
-	if ( dest.mergeAttributes ) {
463  
-		dest.mergeAttributes( src );
464  
-	}
  456
+	// IE6-8 copies events bound via attachEvent when using cloneNode.
  457
+	if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
  458
+		data = jQuery._data( dest );
465 459
 
466  
-	nodeName = dest.nodeName.toLowerCase();
  460
+		for ( e in data.events ) {
  461
+			jQuery.removeEvent( dest, e, data.handle );
  462
+		}
  463
+
  464
+		// Event data gets referenced instead of copied if the expando gets copied too
  465
+		dest.removeAttribute( jQuery.expando );
  466
+	}
467 467
 
468 468
 	// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
469 469
 	if ( nodeName === "script" && dest.text !== src.text ) {
@@ -508,10 +508,6 @@ function cloneFixAttributes( src, dest ) {
508 508
 	} else if ( nodeName === "input" || nodeName === "textarea" ) {
509 509
 		dest.defaultValue = src.defaultValue;
510 510
 	}
511  
-
512  
-	// Event data gets referenced instead of copied if the expando
513  
-	// gets copied too
514  
-	dest.removeAttribute( jQuery.expando );
515 511
 }
516 512
 
517 513
 jQuery.buildFragment = function( args, context, scripts ) {
@@ -636,19 +632,12 @@ jQuery.extend({
636 632
 
637 633
 		if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
638 634
 				(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
639  
-			// IE copies events bound via attachEvent when using cloneNode.
640  
-			// Calling detachEvent on the clone will also remove the events
641  
-			// from the original. In order to get around this, we use some
642  
-			// proprietary methods to clear the events. Thanks to MooTools
643  
-			// guys for this hotness.
644 635
 
645 636
 			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
646 637
 			destElements = getAll( clone );
647 638
 			srcElements = getAll( elem );
648 639
 
649  
-			// Weird iteration because IE will replace the length property
650  
-			// with an element if you are cloning the body and one of the
651  
-			// elements on the page has a name or id of "length"
  640
+			// Fix all IE cloning issues
652 641
 			for ( i = 0; (node = srcElements[i]) != null; ++i ) {
653 642
 				// Ensure that the destination node is not null; Fixes #9587
654 643
 				if ( destElements[i] ) {
@@ -660,8 +649,8 @@ jQuery.extend({
660 649
 		// Copy the events from the original to the clone
661 650
 		if ( dataAndEvents ) {
662 651
 			if ( deepDataAndEvents ) {
663  
-				destElements = getAll( clone );
664  
-				srcElements = getAll( elem );
  652
+				srcElements = srcElements || getAll( elem );
  653
+				destElements = destElements || getAll( clone );
665 654
 
666 655
 				for ( i = 0; (node = srcElements[i]) != null; i++ ) {
667 656
 					cloneCopyEvent( node, destElements[i] );
49  test/unit/event.js
@@ -3104,4 +3104,53 @@ test( "Namespace preserved when passed an Event (#12739)", function() {
3104 3104
 	equal( triggered, 3, "foo.bar triggered" );
3105 3105
 });
3106 3106
 
  3107
+test( "make sure events cloned correctly", 18, function() {
  3108
+	var clone,
  3109
+		fixture = jQuery("#qunit-fixture"),
  3110
+		checkbox = jQuery("#check1"),
  3111
+		p = jQuery("#firstp");
3107 3112
 
  3113
+	fixture.on( "click change", function( event, result ) {
  3114
+		ok( result,  event.type + " on original element is fired" );
  3115
+
  3116
+	}).on( "click", "#firstp", function( event, result ) {
  3117
+		ok( result, "Click on original child element though delegation is fired" );
  3118
+
  3119
+	}).on( "change", "#check1", function( event, result ) {
  3120
+		ok( result, "Change on original child element though delegation is fired" );
  3121
+	});
  3122
+
  3123
+	p.on("click", function( event, result ) {
  3124
+		ok( true, "Click on original child element is fired" );
  3125
+	});
  3126
+
  3127
+	checkbox.on("change", function( event, result ) {
  3128
+		ok( true, "Change on original child element is fired" );
  3129
+	});
  3130
+
  3131
+	fixture.clone().click().change(); // 0 events should be fired
  3132
+
  3133
+	clone = fixture.clone( true );
  3134
+
  3135
+	clone.find("p:first").trigger( "click", true ); // 3 events should fire
  3136
+	clone.find("#check1").trigger( "change", true ); // 3 events should fire
  3137
+	clone.remove();
  3138
+
  3139
+	clone = fixture.clone( true, true );
  3140
+	clone.find("p:first").trigger( "click", true ); // 3 events should fire
  3141
+	clone.find("#check1").trigger( "change", true ); // 3 events should fire
  3142
+
  3143
+	fixture.off();
  3144
+	p.off();
  3145
+	checkbox.off();
  3146
+
  3147
+	p.click(); // 0 should be fired
  3148
+	checkbox.change(); // 0 should be fired
  3149
+
  3150
+	clone.find("p:first").trigger( "click", true ); // 3 events should fire
  3151
+	clone.find("#check1").trigger( "change", true ); // 3 events should fire
  3152
+	clone.remove();
  3153
+
  3154
+	clone.find("p:first").click(); // 0 should be fired
  3155
+	clone.find("#check1").change(); // 0 events should fire
  3156
+});
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.