Skip to content

Commit

Permalink
Landing pull request 332. Appending disconnected radio or checkbox in…
Browse files Browse the repository at this point in the history
…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
timmywil authored and jeresig committed Apr 22, 2011
1 parent 3ac9eb7 commit d274b7b
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/core.js
Expand Up @@ -731,7 +731,7 @@ jQuery.extend({
}
}

// Go thorugh every key on the object,
// Go through every key on the object,
} else {
for ( key in elems ) {
value = callback( elems[ key ], key, arg );
Expand Down
113 changes: 71 additions & 42 deletions src/manipulation.js
Expand Up @@ -70,7 +70,7 @@ jQuery.fn.extend({
}

return elem;
}).append(this);
}).append( this );
}

return this;
Expand Down Expand Up @@ -379,13 +379,13 @@ function cloneCopyEvent( src, dest ) {
}

function cloneFixAttributes( src, dest ) {
var nodeName;

// We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) {
return;
}

var nodeName = dest.nodeName.toLowerCase();

// clearAttributes removes the attributes, which we don't want,
// but also removes the attachEvent events, which we *do* want
if ( dest.clearAttributes ) {
Expand All @@ -398,6 +398,8 @@ function cloneFixAttributes( src, dest ) {
dest.mergeAttributes( src );
}

nodeName = dest.nodeName.toLowerCase();

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

cacheable = true;

cacheresults = jQuery.fragments[ args[0] ];
if ( cacheresults ) {
if ( cacheresults !== 1 ) {
fragment = cacheresults;
}
if ( cacheresults && cacheresults !== 1 ) {
fragment = cacheresults;
}
}

Expand Down Expand Up @@ -508,6 +509,21 @@ function getAll( elem ) {
}
}

// Used in clean, fixes the defaultChecked property
function fixDefaultChecked( elem ) {
if ( elem.type === "checkbox" || elem.type === "radio" ) {
elem.defaultChecked = elem.checked;
}
}
// Finds all inputs and passes them to fixDefaultChecked
function findInputs( elem ) {
if ( jQuery.nodeName( elem, "input" ) ) {
fixDefaultChecked( elem );
} else if ( elem.getElementsByTagName ) {
jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
}
}

jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var clone = elem.cloneNode(true),
Expand Down Expand Up @@ -578,54 +594,67 @@ jQuery.extend({
}

// Convert html string into DOM nodes
if ( typeof elem === "string" && !rhtml.test( elem ) ) {
elem = context.createTextNode( elem );

} else if ( typeof elem === "string" ) {
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(rxhtmlTag, "<$1></$2>");
if ( typeof elem === "string" ) {
if ( !rhtml.test( elem ) ) {
elem = context.createTextNode( elem );
} else {
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(rxhtmlTag, "<$1></$2>");

// Trim whitespace, otherwise indexOf won't work as expected
var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0],
div = context.createElement("div");
// Trim whitespace, otherwise indexOf won't work as expected
var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0],
div = context.createElement("div");

// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];
// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];

// Move to the right depth
while ( depth-- ) {
div = div.lastChild;
}
// Move to the right depth
while ( depth-- ) {
div = div.lastChild;
}

// Remove IE's autoinserted <tbody> from table fragments
if ( !jQuery.support.tbody ) {
// Remove IE's autoinserted <tbody> from table fragments
if ( !jQuery.support.tbody ) {

// String was a <table>, *may* have spurious <tbody>
var hasBody = rtbody.test(elem),
tbody = tag === "table" && !hasBody ?
div.firstChild && div.firstChild.childNodes :
// String was a <table>, *may* have spurious <tbody>
var hasBody = rtbody.test(elem),
tbody = tag === "table" && !hasBody ?
div.firstChild && div.firstChild.childNodes :

// String was a bare <thead> or <tfoot>
wrap[1] === "<table>" && !hasBody ?
div.childNodes :
[];
// String was a bare <thead> or <tfoot>
wrap[1] === "<table>" && !hasBody ?
div.childNodes :
[];

for ( var j = tbody.length - 1; j >= 0 ; --j ) {
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
tbody[ j ].parentNode.removeChild( tbody[ j ] );
for ( var j = tbody.length - 1; j >= 0 ; --j ) {
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
tbody[ j ].parentNode.removeChild( tbody[ j ] );
}
}
}

}
// IE completely kills leading whitespace when innerHTML is used
if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
}

// IE completely kills leading whitespace when innerHTML is used
if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
elem = div.childNodes;
}
}

elem = div.childNodes;
// Resets defaultChecked for any radios and checkboxes
// about to be appended to the DOM in IE 6/7 (#8060)
var len;
if ( !jQuery.support.appendChecked ) {
if ( elem[0] && typeof (len = elem.length) === "number" ) {
for ( i = 0; i < len; i++ ) {
findInputs( elem[i] );
}
} else {
findInputs( elem );
}
}

if ( elem.nodeType ) {
Expand Down
8 changes: 8 additions & 0 deletions src/support.js
Expand Up @@ -186,6 +186,14 @@ jQuery.support = (function() {
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
div.innerHTML = "";

// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM
input = document.createElement("input");
input.setAttribute("type", "checkbox");
input.checked = true;
div.appendChild( input );
support.appendChecked = input.checked;

// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. For more
// info see bug #3333
Expand Down
16 changes: 15 additions & 1 deletion test/unit/manipulation.js
Expand Up @@ -227,7 +227,7 @@ test("unwrap()", function() {
});

var testAppend = function(valueObj) {
expect(37);
expect(40);
var defaultText = "Try them out:"
var result = jQuery("#first").append(valueObj("<b>buga</b>"));
equals( result.text(), defaultText + "buga", "Check if text appending works" );
Expand Down Expand Up @@ -330,6 +330,20 @@ var testAppend = function(valueObj) {
d.contents().appendTo("#nonnodes");
d.remove();
ok( jQuery("#nonnodes").contents().length >= 2, "Check node,textnode,comment append cleanup worked" );

QUnit.reset();
var $input = jQuery("<input />").attr({ "type": "checkbox", "checked": true }).appendTo('#testForm');
equals( $input[0].checked, true, "A checked checkbox that is appended stays checked" );

QUnit.reset();
var $radios = jQuery("input:radio[name='R1']"),
$radioNot = jQuery("<input type='radio' name='R1' checked='checked'/>").insertAfter( $radios ),
$radio = $radios.eq(1).click();
$radioNot[0].checked = false;
$radios.parent().wrap("<div></div>");
equals( $radio[0].checked, true, "Reappending radios uphold which radio is checked" );
equals( $radioNot[0].checked, false, "Reappending radios uphold not being checked" );
QUnit.reset();
}

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

0 comments on commit d274b7b

Please sign in to comment.