diff --git a/lib/api/manipulation.js b/lib/api/manipulation.js index b142fb3adb..d6a81a02f2 100644 --- a/lib/api/manipulation.js +++ b/lib/api/manipulation.js @@ -31,22 +31,51 @@ var _insert = function(concatenator) { if (_.isFunction(elems[0])) { return this.each(function(i, el) { dom = makeDomArray(elems[0].call(el, i, this.html())); - updateDOM(concatenator(dom, el.children || (el.children = [])), el); + concatenator(dom, el.children); + updateDOM(el.children, el); }); } else { return domEach(this, function(i, el) { - updateDOM(concatenator(dom, el.children || (el.children = [])), el); + concatenator(dom, el.children); + updateDOM(el.children, el); }); } }; }; +/* + * Modify an array in-place, removing some number of elements and adding new + * elements directly following them. + * + * @param {Array} array Target array to splice. + * @param {Number} spliceIdx Index at which to begin changing the array. + * @param {Number} spliceCount Number of elements to remove from the array. + * @param {Array} newElems Elements to insert into the array. + * + * @api private + */ +var uniqueSplice = function(array, spliceIdx, spliceCount, newElems) { + var spliceArgs = [spliceIdx, spliceCount].concat(newElems); + var idx, len, prevIdx; + + // Before splicing in new elements, ensure they do not already appear in the + // current array. + for (idx = 0, len = newElems.length; idx < len; ++idx) { + prevIdx = array.indexOf(newElems[idx]); + if (prevIdx > -1) { + array.splice(prevIdx, 1); + } + } + + return array.splice.apply(array, spliceArgs); +}; + var append = exports.append = _insert(function(dom, children) { - return children.concat(dom); + uniqueSplice(children, children.length, 0, dom); }); var prepend = exports.prepend = _insert(function(dom, children) { - return dom.concat(children); + uniqueSplice(children, 0, 0, dom); }); var after = exports.after = function() { @@ -66,7 +95,7 @@ var after = exports.after = function() { } // Add element after `this` element - siblings.splice.apply(siblings, [++index, 0].concat(dom)); + uniqueSplice(siblings, ++index, 0, dom); // Update next, prev, and parent pointers updateDOM(siblings, parent); @@ -92,7 +121,7 @@ var before = exports.before = function() { } // Add element before `el` element - siblings.splice.apply(siblings, [index, 0].concat(dom)); + uniqueSplice(siblings, index, 0, dom); // Update next, prev, and parent pointers updateDOM(siblings, parent); diff --git a/test/api.manipulation.js b/test/api.manipulation.js index 2b618387b5..3f0b9511a4 100644 --- a/test/api.manipulation.js +++ b/test/api.manipulation.js @@ -30,6 +30,20 @@ describe('$(...)', function() { expect($fruits.children(3).hasClass('plum')).to.be.ok(); }); + it('(existing Node) : should remove node from previous location', function() { + var $fruits = $(fruits); + var apple = $fruits.children()[0]; + var $children; + + expect($fruits.children()).to.have.length(3); + $fruits.append(apple); + $children = $fruits.children(); + + expect($children).to.have.length(3); + expect($children[0]).to.not.equal(apple); + expect($children[2]).to.equal(apple); + }); + it('($(...), html) : should add multiple elements as last children', function() { var $fruits = $(fruits); var $plum = $('