Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Landing pull request 332. Appending disconnected radio or checkbox in…

…puts and keeping checked setting Fixes #8060, #8500.

More Details:
 - #332
 - http://bugs.jquery.com/ticket/8060
 - http://bugs.jquery.com/ticket/8500
  • Loading branch information...
commit d274b7b9f7727e8bccd6906d954e4dc790404d23 1 parent 3ac9eb7
authored April 21, 2011 jeresig committed April 21, 2011
2  src/core.js
@@ -731,7 +731,7 @@ jQuery.extend({
731 731
 				}
732 732
 			}
733 733
 
734  
-		// Go thorugh every key on the object,
  734
+		// Go through every key on the object,
735 735
 		} else {
736 736
 			for ( key in elems ) {
737 737
 				value = callback( elems[ key ], key, arg );
113  src/manipulation.js
@@ -70,7 +70,7 @@ jQuery.fn.extend({
70 70
 				}
71 71
 
72 72
 				return elem;
73  
-			}).append(this);
  73
+			}).append( this );
74 74
 		}
75 75
 
76 76
 		return this;
@@ -379,13 +379,13 @@ function cloneCopyEvent( src, dest ) {
379 379
 }
380 380
 
381 381
 function cloneFixAttributes( src, dest ) {
  382
+	var nodeName;
  383
+
382 384
 	// We do not need to do anything for non-Elements
383 385
 	if ( dest.nodeType !== 1 ) {
384 386
 		return;
385 387
 	}
386 388
 
387  
-	var nodeName = dest.nodeName.toLowerCase();
388  
-
389 389
 	// clearAttributes removes the attributes, which we don't want,
390 390
 	// but also removes the attachEvent events, which we *do* want
391 391
 	if ( dest.clearAttributes ) {
@@ -398,6 +398,8 @@ function cloneFixAttributes( src, dest ) {
398 398
 		dest.mergeAttributes( src );
399 399
 	}
400 400
 
  401
+	nodeName = dest.nodeName.toLowerCase();
  402
+
401 403
 	// IE6-8 fail to clone children inside object elements that use
402 404
 	// the proprietary classid attribute value (rather than the type
403 405
 	// attribute) to identify the type of content to display
@@ -446,11 +448,10 @@ jQuery.buildFragment = function( args, nodes, scripts ) {
446 448
 		args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
447 449
 
448 450
 		cacheable = true;
  451
+
449 452
 		cacheresults = jQuery.fragments[ args[0] ];
450  
-		if ( cacheresults ) {
451  
-			if ( cacheresults !== 1 ) {
452  
-				fragment = cacheresults;
453  
-			}
  453
+		if ( cacheresults && cacheresults !== 1 ) {
  454
+			fragment = cacheresults;
454 455
 		}
455 456
 	}
456 457
 
@@ -508,6 +509,21 @@ function getAll( elem ) {
508 509
 	}
509 510
 }
510 511
 
  512
+// Used in clean, fixes the defaultChecked property
  513
+function fixDefaultChecked( elem ) {
  514
+	if ( elem.type === "checkbox" || elem.type === "radio" ) {
  515
+		elem.defaultChecked = elem.checked;
  516
+	}
  517
+}
  518
+// Finds all inputs and passes them to fixDefaultChecked
  519
+function findInputs( elem ) {
  520
+	if ( jQuery.nodeName( elem, "input" ) ) {
  521
+		fixDefaultChecked( elem );
  522
+	} else if ( elem.getElementsByTagName ) {
  523
+		jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
  524
+	}
  525
+}
  526
+
511 527
 jQuery.extend({
512 528
 	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
513 529
 		var clone = elem.cloneNode(true),
@@ -578,54 +594,67 @@ jQuery.extend({
578 594
 			}
579 595
 
580 596
 			// Convert html string into DOM nodes
581  
-			if ( typeof elem === "string" && !rhtml.test( elem ) ) {
582  
-				elem = context.createTextNode( elem );
583  
-
584  
-			} else if ( typeof elem === "string" ) {
585  
-				// Fix "XHTML"-style tags in all browsers
586  
-				elem = elem.replace(rxhtmlTag, "<$1></$2>");
  597
+			if ( typeof elem === "string" ) {
  598
+				if ( !rhtml.test( elem ) ) {
  599
+					elem = context.createTextNode( elem );
  600
+				} else {
  601
+					// Fix "XHTML"-style tags in all browsers
  602
+					elem = elem.replace(rxhtmlTag, "<$1></$2>");
587 603
 
588  
-				// Trim whitespace, otherwise indexOf won't work as expected
589  
-				var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
590  
-					wrap = wrapMap[ tag ] || wrapMap._default,
591  
-					depth = wrap[0],
592  
-					div = context.createElement("div");
  604
+					// Trim whitespace, otherwise indexOf won't work as expected
  605
+					var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
  606
+						wrap = wrapMap[ tag ] || wrapMap._default,
  607
+						depth = wrap[0],
  608
+						div = context.createElement("div");
593 609
 
594  
-				// Go to html and back, then peel off extra wrappers
595  
-				div.innerHTML = wrap[1] + elem + wrap[2];
  610
+					// Go to html and back, then peel off extra wrappers
  611
+					div.innerHTML = wrap[1] + elem + wrap[2];
596 612
 
597  
-				// Move to the right depth
598  
-				while ( depth-- ) {
599  
-					div = div.lastChild;
600  
-				}
  613
+					// Move to the right depth
  614
+					while ( depth-- ) {
  615
+						div = div.lastChild;
  616
+					}
601 617
 
602  
-				// Remove IE's autoinserted <tbody> from table fragments
603  
-				if ( !jQuery.support.tbody ) {
  618
+					// Remove IE's autoinserted <tbody> from table fragments
  619
+					if ( !jQuery.support.tbody ) {
604 620
 
605  
-					// String was a <table>, *may* have spurious <tbody>
606  
-					var hasBody = rtbody.test(elem),
607  
-						tbody = tag === "table" && !hasBody ?
608  
-							div.firstChild && div.firstChild.childNodes :
  621
+						// String was a <table>, *may* have spurious <tbody>
  622
+						var hasBody = rtbody.test(elem),
  623
+							tbody = tag === "table" && !hasBody ?
  624
+								div.firstChild && div.firstChild.childNodes :
609 625
 
610  
-							// String was a bare <thead> or <tfoot>
611  
-							wrap[1] === "<table>" && !hasBody ?
612  
-								div.childNodes :
613  
-								[];
  626
+								// String was a bare <thead> or <tfoot>
  627
+								wrap[1] === "<table>" && !hasBody ?
  628
+									div.childNodes :
  629
+									[];
614 630
 
615  
-					for ( var j = tbody.length - 1; j >= 0 ; --j ) {
616  
-						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
617  
-							tbody[ j ].parentNode.removeChild( tbody[ j ] );
  631
+						for ( var j = tbody.length - 1; j >= 0 ; --j ) {
  632
+							if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
  633
+								tbody[ j ].parentNode.removeChild( tbody[ j ] );
  634
+							}
618 635
 						}
619 636
 					}
620 637
 
621  
-				}
  638
+					// IE completely kills leading whitespace when innerHTML is used
  639
+					if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
  640
+						div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
  641
+					}
622 642
 
623  
-				// IE completely kills leading whitespace when innerHTML is used
624  
-				if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
625  
-					div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
  643
+					elem = div.childNodes;
626 644
 				}
  645
+			}
627 646
 
628  
-				elem = div.childNodes;
  647
+			// Resets defaultChecked for any radios and checkboxes
  648
+			// about to be appended to the DOM in IE 6/7 (#8060)
  649
+			var len;
  650
+			if ( !jQuery.support.appendChecked ) {
  651
+				if ( elem[0] && typeof (len = elem.length) === "number" ) {
  652
+					for ( i = 0; i < len; i++ ) {
  653
+						findInputs( elem[i] );
  654
+					}
  655
+				} else {
  656
+					findInputs( elem );
  657
+				}
629 658
 			}
630 659
 
631 660
 			if ( elem.nodeType ) {
8  src/support.js
@@ -186,6 +186,14 @@ jQuery.support = (function() {
186 186
 	support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
187 187
 	div.innerHTML = "";
188 188
 
  189
+	// Check if a disconnected checkbox will retain its checked
  190
+	// value of true after appended to the DOM
  191
+	input = document.createElement("input");
  192
+	input.setAttribute("type", "checkbox");
  193
+	input.checked = true;
  194
+	div.appendChild( input );
  195
+	support.appendChecked = input.checked;
  196
+
189 197
 	// Check if div with explicit width and no margin-right incorrectly
190 198
 	// gets computed margin-right based on width of container. For more
191 199
 	// info see bug #3333
16  test/unit/manipulation.js
@@ -227,7 +227,7 @@ test("unwrap()", function() {
227 227
 });
228 228
 
229 229
 var testAppend = function(valueObj) {
230  
-	expect(37);
  230
+	expect(40);
231 231
 	var defaultText = "Try them out:"
232 232
 	var result = jQuery("#first").append(valueObj("<b>buga</b>"));
233 233
 	equals( result.text(), defaultText + "buga", "Check if text appending works" );
@@ -330,6 +330,20 @@ var testAppend = function(valueObj) {
330 330
 	d.contents().appendTo("#nonnodes");
331 331
 	d.remove();
332 332
 	ok( jQuery("#nonnodes").contents().length >= 2, "Check node,textnode,comment append cleanup worked" );
  333
+
  334
+	QUnit.reset();
  335
+	var $input = jQuery("<input />").attr({ "type": "checkbox", "checked": true }).appendTo('#testForm');
  336
+	equals( $input[0].checked, true, "A checked checkbox that is appended stays checked" );
  337
+
  338
+	QUnit.reset();
  339
+	var $radios = jQuery("input:radio[name='R1']"),
  340
+		$radioNot = jQuery("<input type='radio' name='R1' checked='checked'/>").insertAfter( $radios ),
  341
+		$radio = $radios.eq(1).click();
  342
+	$radioNot[0].checked = false;
  343
+	$radios.parent().wrap("<div></div>");
  344
+	equals( $radio[0].checked, true, "Reappending radios uphold which radio is checked" );
  345
+	equals( $radioNot[0].checked, false, "Reappending radios uphold not being checked" );
  346
+	QUnit.reset();
333 347
 }
334 348
 
335 349
 test("append(String|Element|Array&lt;Element&gt;|jQuery)", function() {

0 notes on commit d274b7b

Please sign in to comment.
Something went wrong with that request. Please try again.