Speed up & compatibility improvements for new clone mechanism in IE.
authorColin Snover <github.com@zetafleet.com>
Mon, 13 Dec 2010 01:23:22 +0000 (19:23 -0600)
committerColin Snover <github.com@zetafleet.com>
Mon, 13 Dec 2010 01:23:22 +0000 (19:23 -0600)
src/manipulation.js
test/index.html
test/unit/manipulation.js

index 3895a7a..24409d2 100644 (file)
@@ -193,28 +193,20 @@ jQuery.fn.extend({
                                // from the original. In order to get around this, we use some
                                // proprietary methods to clear the events. Thanks to MooTools
                                // guys for this hotness.
-                               var srcElements = jQuery(this).find('*').andSelf();
-                               jQuery(clone).find('*').andSelf().each(function (i, clone) {
-                                       // We do not need to do anything for non-Elements
-                                       if (this.nodeType !== 1) {
-                                               return;
-                                       }
 
-                                       // clearAttributes removes the attributes, but also
-                                       // removes the attachEvent events
-                                       clone.clearAttributes();
+                               // Using Sizzle here is crazy slow, so we use getElementsByTagName
+                               // instead
+                               var srcElements = this.getElementsByTagName("*"),
+                                       destElements = clone.getElementsByTagName("*");
 
-                                       // mergeAttributes only merges back on the original attributes,
-                                       // not the events
-                                       clone.mergeAttributes(srcElements[i]);
+                               // Weird iteration because IE will replace the length property
+                               // with an element if you are cloning the body and one of the
+                               // elements on the page has a name or id of "length"
+                               for ( var i = 0; srcElements[i]; ++i ) {
+                                       cloneFixAttributes( srcElements[i], destElements[i] );
+                               }
 
-                                       // 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
-                                       if (clone.nodeName.toLowerCase() === 'object') {
-                                               clone.outerHTML = srcElements[i].outerHTML;
-                                       }
-                               });
+                               cloneFixAttributes( this, clone );
                        }
 
                        return clone;
@@ -355,7 +347,7 @@ jQuery.fn.extend({
                                                        root(this[i], first) :
                                                        this[i],
                                                i > 0 || results.cacheable || this.length > 1  ?
-                                                       fragment.cloneNode(true) :
+                                                       jQuery(fragment).clone(true)[0] :
                                                        fragment
                                        );
                                }
@@ -402,6 +394,46 @@ function cloneCopyEvent(orig, ret) {
        });
 }
 
+function cloneFixAttributes(src, dest) {
+       // 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
+       dest.clearAttributes();
+
+       // mergeAttributes, in contrast, only merges back on the
+       // original attributes, not the events
+       dest.mergeAttributes(src);
+
+       // 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
+       if ( nodeName === "object" ) {
+               dest.outerHTML = src.outerHTML;
+       }
+
+       // IE6-? fails to persist the checked state of a cloned checkbox
+       // or radio button
+       else if ( nodeName === "input" && src.checked ) {
+               dest.defaultChecked = dest.checked = src.checked;
+       }
+
+       // IE6-? fails to return the selected option to the default selected
+       // state when cloning options
+       else if ( nodeName === "option" ) {
+               dest.selected = src.defaultSelected;
+       }
+
+       // Event data gets referenced instead of copied if the expando
+       // gets copied too
+       dest.removeAttribute( jQuery.expando );
+}
+
 jQuery.buildFragment = function( args, nodes, scripts ) {
        var fragment, cacheable, cacheresults,
                doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
index bd11ebb..238b7d5 100644 (file)
@@ -263,7 +263,7 @@ Z</textarea>
 
                        <div id="slidetogglein" class='chain test'>slideToggleIn<div>slideToggleIn</div></div>
                        <div id="slidetoggleout" class='chain test out'>slideToggleOut<div>slideToggleOut</div></div>
-                       
+
                        <div id="fadetogglein" class='chain test'>fadeToggleIn<div>fadeToggleIn</div></div>
       <div id="fadetoggleout" class='chain test out'>fadeToggleOut<div>fadeToggleOut</div></div>
 
index 6c3ecf8..d49029e 100644 (file)
@@ -382,7 +382,7 @@ test("append(Function) with incoming value", function() {
 });
 
 test("append the same fragment with events (Bug #6997, 5566)", function () {
-       expect(2 + (document.fireEvent ? 1 : 0));
+       expect(4 + (document.fireEvent ? 1 : 0));
        stop(1000);
 
        var element;
@@ -413,6 +413,14 @@ test("append the same fragment with events (Bug #6997, 5566)", function () {
 
        jQuery("#listWithTabIndex li").before(element);
        jQuery("#listWithTabIndex li.test6997").eq(1).click();
+
+       element = jQuery("<select><option>Foo</option><option selected>Bar</option></select>");
+
+       equals( element.clone().find("option:selected").val(), element.find("option:selected").val(), "Selected option cloned correctly" );
+
+       element = jQuery("<input type='checkbox'>").attr('checked', 'checked');
+
+       equals( element.clone().is(":checked"), element.is(":checked"), "Checked input cloned correctly" );
 });
 
 test("appendTo(String|Element|Array&lt;Element&gt;|jQuery)", function() {