X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fmanipulation.js;h=96caa02d0b2948a14b91343b44b0d8900383c890;hb=c43b078c6911027fd4124d542446ad0098662f6a;hp=3895a7a0156d585d0d9cb59f7dbea4b617cc0058;hpb=4fae75d575b20d887e4a273c7991c55f8821a62c;p=jquery.git diff --git a/src/manipulation.js b/src/manipulation.js index 3895a7a..96caa02 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 ); } @@ -378,14 +370,18 @@ function root( elem, cur ) { } function cloneCopyEvent(orig, ret) { - var i = 0; - - ret.each(function() { - if ( this.nodeType !== 1 || this.nodeName !== (orig[i] && orig[i].nodeName) ) { + ret.each(function (nodeIndex) { + if ( this.nodeType !== 1 || !jQuery.hasData(orig[nodeIndex]) ) { return; } - var oldData = jQuery.data( orig[i++] ), + // XXX remove for 1.5 RC or merge back in if there is actually a reason for this check that has been + // unexposed by unit tests + if ( this.nodeName !== (orig[nodeIndex] && orig[nodeIndex].nodeName) ) { + throw "Cloned data mismatch"; + } + + var oldData = jQuery.data( orig[nodeIndex] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events; @@ -394,24 +390,62 @@ function cloneCopyEvent(orig, ret) { curData.events = {}; for ( var type in events ) { - for ( var handler in events[ type ] ) { - jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + for ( var i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( this, type, events[ type ][ i ], events[ type ][ i ].data ); } } } }); } +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-8 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-8 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); - // Only cache "small" (1/2 KB) strings that are associated with the main document + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document // Cloning options loses the selected state, so don't cache them // IE 6 doesn't like it when you put or elements in a fragment // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && - !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; cacheresults = jQuery.fragments[ args[0] ];