Skip to content

Commit d274b7b

Browse files
timmywiljeresig
timmywil
authored andcommitted
Landing pull request 332. Appending disconnected radio or checkbox inputs and keeping checked setting Fixes #8060, #8500.
More Details: - jquery#332 - http://bugs.jquery.com/ticket/8060 - http://bugs.jquery.com/ticket/8500
1 parent 3ac9eb7 commit d274b7b

File tree

4 files changed

+95
-44
lines changed

4 files changed

+95
-44
lines changed

src/core.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ jQuery.extend({
731731
}
732732
}
733733

734-
// Go thorugh every key on the object,
734+
// Go through every key on the object,
735735
} else {
736736
for ( key in elems ) {
737737
value = callback( elems[ key ], key, arg );

src/manipulation.js

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jQuery.fn.extend({
7070
}
7171

7272
return elem;
73-
}).append(this);
73+
}).append( this );
7474
}
7575

7676
return this;
@@ -379,13 +379,13 @@ function cloneCopyEvent( src, dest ) {
379379
}
380380

381381
function cloneFixAttributes( src, dest ) {
382+
var nodeName;
383+
382384
// We do not need to do anything for non-Elements
383385
if ( dest.nodeType !== 1 ) {
384386
return;
385387
}
386388

387-
var nodeName = dest.nodeName.toLowerCase();
388-
389389
// clearAttributes removes the attributes, which we don't want,
390390
// but also removes the attachEvent events, which we *do* want
391391
if ( dest.clearAttributes ) {
@@ -398,6 +398,8 @@ function cloneFixAttributes( src, dest ) {
398398
dest.mergeAttributes( src );
399399
}
400400

401+
nodeName = dest.nodeName.toLowerCase();
402+
401403
// IE6-8 fail to clone children inside object elements that use
402404
// the proprietary classid attribute value (rather than the type
403405
// attribute) to identify the type of content to display
@@ -446,11 +448,10 @@ jQuery.buildFragment = function( args, nodes, scripts ) {
446448
args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
447449

448450
cacheable = true;
451+
449452
cacheresults = jQuery.fragments[ args[0] ];
450-
if ( cacheresults ) {
451-
if ( cacheresults !== 1 ) {
452-
fragment = cacheresults;
453-
}
453+
if ( cacheresults && cacheresults !== 1 ) {
454+
fragment = cacheresults;
454455
}
455456
}
456457

@@ -508,6 +509,21 @@ function getAll( elem ) {
508509
}
509510
}
510511

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+
511527
jQuery.extend({
512528
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
513529
var clone = elem.cloneNode(true),
@@ -578,54 +594,67 @@ jQuery.extend({
578594
}
579595

580596
// 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>");
587603

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");
593609

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];
596612

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+
}
601617

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 ) {
604620

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 :
609625

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+
[];
614630

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+
}
618635
}
619636
}
620637

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+
}
622642

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;
626644
}
645+
}
627646

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+
}
629658
}
630659

631660
if ( elem.nodeType ) {

src/support.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,14 @@ jQuery.support = (function() {
186186
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
187187
div.innerHTML = "";
188188

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+
189197
// Check if div with explicit width and no margin-right incorrectly
190198
// gets computed margin-right based on width of container. For more
191199
// info see bug #3333

test/unit/manipulation.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ test("unwrap()", function() {
227227
});
228228

229229
var testAppend = function(valueObj) {
230-
expect(37);
230+
expect(40);
231231
var defaultText = "Try them out:"
232232
var result = jQuery("#first").append(valueObj("<b>buga</b>"));
233233
equals( result.text(), defaultText + "buga", "Check if text appending works" );
@@ -330,6 +330,20 @@ var testAppend = function(valueObj) {
330330
d.contents().appendTo("#nonnodes");
331331
d.remove();
332332
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();
333347
}
334348

335349
test("append(String|Element|Array&lt;Element&gt;|jQuery)", function() {

0 commit comments

Comments
 (0)