From 1a3fd3329dde3c61dc4b2b14792fb9a61b637893 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Sun, 12 Dec 2010 19:23:22 -0600 Subject: [PATCH] Speed up & compatibility improvements for new clone mechanism in IE. --- src/manipulation.js | 72 ++++++++++++++++++++++++++++++++------------- test/index.html | 2 +- test/unit/manipulation.js | 10 ++++++- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/manipulation.js b/src/manipulation.js index 3895a7a..24409d2 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -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); diff --git a/test/index.html b/test/index.html index bd11ebb..238b7d5 100644 --- a/test/index.html +++ b/test/index.html @@ -263,7 +263,7 @@ Z
slideToggleIn
slideToggleIn
slideToggleOut
slideToggleOut
- +
fadeToggleIn
fadeToggleIn
fadeToggleOut
fadeToggleOut
diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 6c3ecf8..d49029e 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -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(""); + + equals( element.clone().find("option:selected").val(), element.find("option:selected").val(), "Selected option cloned correctly" ); + + element = jQuery("").attr('checked', 'checked'); + + equals( element.clone().is(":checked"), element.is(":checked"), "Checked input cloned correctly" ); }); test("appendTo(String|Element|Array<Element>|jQuery)", function() { -- 1.7.10.4