Commit d274b7b9 authored by timmywil's avatar timmywil Committed by jeresig

Landing pull request 332. Appending disconnected radio or checkbox inputs and...

Landing pull request 332. Appending disconnected radio or checkbox inputs and keeping checked setting Fixes #8060, #8500.

More Details:
 - https://github.com/jquery/jquery/pull/332
 - http://bugs.jquery.com/ticket/8060
 - http://bugs.jquery.com/ticket/8500
parent 3ac9eb7c
...@@ -731,7 +731,7 @@ jQuery.extend({ ...@@ -731,7 +731,7 @@ jQuery.extend({
} }
} }
// Go thorugh every key on the object, // Go through every key on the object,
} else { } else {
for ( key in elems ) { for ( key in elems ) {
value = callback( elems[ key ], key, arg ); value = callback( elems[ key ], key, arg );
......
...@@ -70,7 +70,7 @@ jQuery.fn.extend({ ...@@ -70,7 +70,7 @@ jQuery.fn.extend({
} }
return elem; return elem;
}).append(this); }).append( this );
} }
return this; return this;
...@@ -379,13 +379,13 @@ function cloneCopyEvent( src, dest ) { ...@@ -379,13 +379,13 @@ function cloneCopyEvent( src, dest ) {
} }
function cloneFixAttributes( src, dest ) { function cloneFixAttributes( src, dest ) {
var nodeName;
// We do not need to do anything for non-Elements // We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) { if ( dest.nodeType !== 1 ) {
return; return;
} }
var nodeName = dest.nodeName.toLowerCase();
// clearAttributes removes the attributes, which we don't want, // clearAttributes removes the attributes, which we don't want,
// but also removes the attachEvent events, which we *do* want // but also removes the attachEvent events, which we *do* want
if ( dest.clearAttributes ) { if ( dest.clearAttributes ) {
...@@ -398,6 +398,8 @@ function cloneFixAttributes( src, dest ) { ...@@ -398,6 +398,8 @@ function cloneFixAttributes( src, dest ) {
dest.mergeAttributes( src ); dest.mergeAttributes( src );
} }
nodeName = dest.nodeName.toLowerCase();
// IE6-8 fail to clone children inside object elements that use // IE6-8 fail to clone children inside object elements that use
// the proprietary classid attribute value (rather than the type // the proprietary classid attribute value (rather than the type
// attribute) to identify the type of content to display // attribute) to identify the type of content to display
...@@ -446,11 +448,10 @@ jQuery.buildFragment = function( args, nodes, scripts ) { ...@@ -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] )) ) { args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
cacheable = true; cacheable = true;
cacheresults = jQuery.fragments[ args[0] ]; cacheresults = jQuery.fragments[ args[0] ];
if ( cacheresults ) { if ( cacheresults && cacheresults !== 1 ) {
if ( cacheresults !== 1 ) { fragment = cacheresults;
fragment = cacheresults;
}
} }
} }
...@@ -508,6 +509,21 @@ function getAll( elem ) { ...@@ -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({ jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) { clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var clone = elem.cloneNode(true), var clone = elem.cloneNode(true),
...@@ -578,54 +594,67 @@ jQuery.extend({ ...@@ -578,54 +594,67 @@ jQuery.extend({
} }
// Convert html string into DOM nodes // Convert html string into DOM nodes
if ( typeof elem === "string" && !rhtml.test( elem ) ) { if ( typeof elem === "string" ) {
elem = context.createTextNode( elem ); if ( !rhtml.test( elem ) ) {
elem = context.createTextNode( elem );
} else if ( typeof elem === "string" ) { } else {
// Fix "XHTML"-style tags in all browsers // Fix "XHTML"-style tags in all browsers
elem = elem.replace(rxhtmlTag, "<$1></$2>"); elem = elem.replace(rxhtmlTag, "<$1></$2>");
// Trim whitespace, otherwise indexOf won't work as expected // Trim whitespace, otherwise indexOf won't work as expected
var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(), var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default, wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0], depth = wrap[0],
div = context.createElement("div"); div = context.createElement("div");
// Go to html and back, then peel off extra wrappers // Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2]; div.innerHTML = wrap[1] + elem + wrap[2];
// Move to the right depth // Move to the right depth
while ( depth-- ) { while ( depth-- ) {
div = div.lastChild; div = div.lastChild;
} }
// Remove IE's autoinserted <tbody> from table fragments // Remove IE's autoinserted <tbody> from table fragments
if ( !jQuery.support.tbody ) { if ( !jQuery.support.tbody ) {
// String was a <table>, *may* have spurious <tbody> // String was a <table>, *may* have spurious <tbody>
var hasBody = rtbody.test(elem), var hasBody = rtbody.test(elem),
tbody = tag === "table" && !hasBody ? tbody = tag === "table" && !hasBody ?
div.firstChild && div.firstChild.childNodes : div.firstChild && div.firstChild.childNodes :
// String was a bare <thead> or <tfoot> // String was a bare <thead> or <tfoot>
wrap[1] === "<table>" && !hasBody ? wrap[1] === "<table>" && !hasBody ?
div.childNodes : div.childNodes :
[]; [];
for ( var j = tbody.length - 1; j >= 0 ; --j ) { for ( var j = tbody.length - 1; j >= 0 ; --j ) {
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
tbody[ j ].parentNode.removeChild( tbody[ j ] ); 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 elem = div.childNodes;
if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
} }
}
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 ) { if ( elem.nodeType ) {
......
...@@ -186,6 +186,14 @@ jQuery.support = (function() { ...@@ -186,6 +186,14 @@ jQuery.support = (function() {
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
div.innerHTML = ""; 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 // Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. For more // gets computed margin-right based on width of container. For more
// info see bug #3333 // info see bug #3333
......
...@@ -227,7 +227,7 @@ test("unwrap()", function() { ...@@ -227,7 +227,7 @@ test("unwrap()", function() {
}); });
var testAppend = function(valueObj) { var testAppend = function(valueObj) {
expect(37); expect(40);
var defaultText = "Try them out:" var defaultText = "Try them out:"
var result = jQuery("#first").append(valueObj("<b>buga</b>")); var result = jQuery("#first").append(valueObj("<b>buga</b>"));
equals( result.text(), defaultText + "buga", "Check if text appending works" ); equals( result.text(), defaultText + "buga", "Check if text appending works" );
...@@ -330,6 +330,20 @@ var testAppend = function(valueObj) { ...@@ -330,6 +330,20 @@ var testAppend = function(valueObj) {
d.contents().appendTo("#nonnodes"); d.contents().appendTo("#nonnodes");
d.remove(); d.remove();
ok( jQuery("#nonnodes").contents().length >= 2, "Check node,textnode,comment append cleanup worked" ); 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() { test("append(String|Element|Array&lt;Element&gt;|jQuery)", function() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment