diff --git a/src/manipulation.js b/src/manipulation.js index 3031c4f73..4f2cd1b1c 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -274,14 +274,13 @@ jQuery.extend({ return clone; }, - buildFragment: function( elems, context, scripts, selection ) { + buildFragment: function( elems, context, scripts, selection, ignored ) { var j, elem, contains, tmp, tag, wrap, l = elems.length, // Ensure a safe fragment safe = createSafeFragment( context ), - nodes = [], i = 0; @@ -343,9 +342,11 @@ jQuery.extend({ i = 0; while ( (elem = nodes[ i++ ]) ) { - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything + // Skip elements already in the context collection (trac-4087) if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } continue; } @@ -573,28 +574,28 @@ jQuery.fn.extend({ }, replaceWith: function() { - var arg = arguments[ 0 ]; + var ignored = []; - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; + // Make the changes, replacing each non-ignored context element with the new content + return this.domManip( arguments, function( elem ) { + var parent = this.parentNode; - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } } - }); - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); + // Force callback invocation + }, ignored ); }, detach: function( selector ) { return this.remove( selector, true ); }, - domManip: function( args, callback ) { + domManip: function( args, callback, ignored ) { // Flatten any nested arrays args = concat.apply( [], args ); @@ -617,19 +618,20 @@ jQuery.fn.extend({ if ( isFunction ) { args[0] = value.call( this, index, self.html() ); } - self.domManip( args, callback ); + self.domManip( args, callback, ignored ); }); } if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this, ignored ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } - if ( first ) { + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 8ce29b3a9..12ea6194b 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -1329,15 +1329,20 @@ test( "replaceWith(string) for more than one element", function() { equal(jQuery("#foo p").length, 0, "verify that all the three original element have been replaced"); }); -test( "Empty replaceWith (#13401; #13596)", 8, function() { - var $el = jQuery( "
" ), +test( "Empty replaceWith (trac-13401; trac-13596; gh-2204)", function() { + + expect( 25 ); + + var $el = jQuery( "" ).html( "0
" ), + expectedHTML = $el.html(), tests = { "empty string": "", "empty array": [], + "array of empty string": [ "" ], "empty collection": jQuery( "#nonexistent" ), - // in case of jQuery(...).replaceWith(); - "empty undefined": undefined + // in case of jQuery(...).replaceWith(); + "undefined": undefined }; jQuery.each( tests, function( label, input ) { @@ -1345,6 +1350,17 @@ test( "Empty replaceWith (#13401; #13596)", 8, function() { strictEqual( $el.html(), "", "replaceWith(" + label + ")" ); $el.html( "" ).children().replaceWith(function() { return input; }); strictEqual( $el.html(), "", "replaceWith(function returning " + label + ")" ); + $el.html( "" ).children().replaceWith(function( i ) { i; return input; }); + strictEqual( $el.html(), "", "replaceWith(other function returning " + label + ")" ); + $el.html( "" ).children().replaceWith(function( i ) { + return i ? + input : + jQuery( this ).html( i + "" ); + }); + strictEqual( $el.eq( 0 ).html(), expectedHTML, + "replaceWith(function conditionally returning context)" ); + strictEqual( $el.eq( 1 ).html(), "", + "replaceWith(function conditionally returning " + label + ")" ); }); });