From d274b7b9f7727e8bccd6906d954e4dc790404d23 Mon Sep 17 00:00:00 2001 From: timmywil Date: Thu, 21 Apr 2011 21:51:23 -0400 Subject: [PATCH] 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 --- src/core.js | 2 +- src/manipulation.js | 113 ++++++++++++++++++++++++-------------- src/support.js | 8 +++ test/unit/manipulation.js | 16 +++++- 4 files changed, 95 insertions(+), 44 deletions(-) diff --git a/src/core.js b/src/core.js index a82a2fdce..8d812e384 100644 --- a/src/core.js +++ b/src/core.js @@ -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 ); diff --git a/src/manipulation.js b/src/manipulation.js index f519b4d35..610d19b7a 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -70,7 +70,7 @@ jQuery.fn.extend({ } return elem; - }).append(this); + }).append( this ); } return this; @@ -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 ) { @@ -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 @@ -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; } } @@ -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), @@ -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>"); + 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>"); - // 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 from table fragments - if ( !jQuery.support.tbody ) { + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { - // String was a , *may* have spurious - var hasBody = rtbody.test(elem), - tbody = tag === "table" && !hasBody ? - div.firstChild && div.firstChild.childNodes : + // String was a
, *may* have spurious + var hasBody = rtbody.test(elem), + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : - // String was a bare or - wrap[1] === "
" && !hasBody ? - div.childNodes : - []; + // String was a bare or + wrap[1] === "
" && !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 ) { diff --git a/src/support.js b/src/support.js index b286907ff..8f2beebbc 100644 --- a/src/support.js +++ b/src/support.js @@ -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 diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index e0fb369a6..b71b6962e 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -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("buga")); equals( result.text(), defaultText + "buga", "Check if text appending works" ); @@ -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("").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("").insertAfter( $radios ), + $radio = $radios.eq(1).click(); + $radioNot[0].checked = false; + $radios.parent().wrap("
"); + 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<Element>|jQuery)", function() { -- 2.39.5