Did you know that .append() sometimes moves the nodes you pass into it, and other times clones them? This undocumented behavior may cause you some grief if you’re assuming the first, but seeing the second.
If you’re appending to more than 1 node, then jQuery clones the input and then appends the clones to each of the selected items. If you’re appending to just 1 node, then jQuery does not clone the input, but instead moves the item to that place in the document. These two behaviors can leave you with significantly different results.
To help clarify the situation, and fill the documentation hole, I’ll examine the how the following HTML is affected for each of the two .append() behaviors.
[code lang=”html”]
<html>
<head>
<!– scripts will go here –>
</head>
<body>
<p>original text</p>
<div></div>
<div></div>
</body>
</html>
[/code]
.append() with cloning
First, let’s take a look at a script that will clone nodes and append them. Demo.
[code lang=”javascript”]
$(function(){
// select the only <p> in the document
var $dom = $("p");
var actualNode = $dom.get(0);
actualNode.iAmReal = true;
// append the node to a selection
// of *two* <div> nodes
$("body div").append($dom);
// now set the text on our original <p>
// selection. this will only show up if the
// <p> is *not* a clone
$dom.text("updated text");
$appendedDom = $("body div p");
// get the node that’s *really*
// in the <div> tag
actualAppendedNode = $appendedDom.get(0);
// this will alert "moved!" if it’s
// the real node, "cloned!" otherwise
alert(typeof(actualAppendedNode.iAmReal) == "undefined" ? "cloned!" : "moved!");
});
[/code]
In the above example, I select the single <p> in the document, and append it to a selection of two divs. I then update the text in the original <p> selection. What is the output, you ask? The original <p> remains the first node of the <body>, and only it’s text is updated. A cloned <p> tag is added to each of the two <div>s, and the text for these 2 <p> tags is not updated.
So, selecting 1 <p> and appending to 2 <div> tags leaves you with a total of 3 <p> tags.
The resulting DOM structure looks like:
[code lang=”html”]
<html>
<head>
</head>
<body>
<p>updated text</p>
<div>
<p>original text</p>
</div>
<div>
<p>original text</p>
</div>
</body>
</html>
[/code]
.append() without cloning
Next, let’s take a look at a script that will move nodes instead of clone them. Demo.
[code lang=”javascript”]
$(function(){
var $dom = $("p");
var actualNode = $dom.get(0);
actualNode.iAmReal = true;
// append the node to a selection
// of *one* node
$("body div:first").append($dom);
// now set the text on the original item
// this will only show up if it’s not
// a clone
$dom.text("updated text");
$appendedDom = $("body div:first p");
// get the node that’s *really*
// in the <div> tag
actualAppendedNode = $appendedDom.get(0);
// this will alert "moved!" if it’s
// the real node, "clonded!" otherwise
alert(typeof(actualAppendedNode.iAmReal) == "undefined" ? "cloned!" : "moved!");
});
[/code]
In this above example, I’ve changed the <div> selector to include only a single div instead of both. Since jQuery is now appending the selected <p> to a single <div> the behavior is changed, and the <p> is not cloned, but instead is moved. Line 13 updates the <p>’s text, just as you’d imagine.
So, selecting 1 <p> and appending to 1 <div> tag leaves you with a total of 1 <p> tag.
The resulting DOM structure looks like:
[code lang=”html”]
<html>
<head>
</head>
<body>
<div>
<p>updated text</p>
</div>
<div></div>
</body>
</html>
[/code]
summary
jQuery’s .append() behaves different on it’s input depending on if it’s appending to a single node or to multiple nodes. Appending to more than 1 target node:
- will not remove the input from the DOM
- will clone the input and append to the selection
but, appending to exactly 1 target node does the opposite:
- will remove the input from the DOM
- will not clone the input and append to the selection
As far as I know, this behavior is true for all functions in the Manipulation area of the docs.
the undocumented life of jQuery’s .append() – http://tinyurl.com/c78nmz
This comment was originally posted on Twitter
The undocumented life of jQuery’s .append() – http://bit.ly/rPtk
This comment was originally posted on Twitter
The undocumented life of jQuery’s .append http://tinyurl.com/c78nmz #query #tech
This comment was originally posted on Twitter
The undocumented life of jQuery’s .append() http://twurl.nl/1z11ok
This comment was originally posted on Twitter
I tried to understand this. Even re-read it. I am not the master you are. 🙂
… I guess I’ll have to stick to Buck’s JQuery for People Who Can’t Program Good.
Code in jQuery? The undocumented life of jQuery’s .append() – http://bit.ly/rPtk
This comment was originally posted on Twitter
[software] Code in jQuery? The undocumented life of jQuery’s .append() http://bit.ly/UQf6T
This comment was originally posted on Twitter
I don’t know if it works for others, but I need to remove a child element, check for scrolling, then replace that child and THEN prepend a copy of that child to another div.
I create TWO clones, one to reinsert into its parent, another to prepend to another different HTML element:
//get the last child of the current node
//firstcontainer is a div that contains html
nodeChildren = $(firstcontainer).children();
tmpChild = $(nodeChildren)[$(nodeChildren).length-1];
//a clone of the current node’s last child
curChild = $(tmpChild).clone();
//a second clone to use elsewhere
curChild2 = $(tmpChild).clone();
//remove child element from parent elem
$(tmpChild).remove();
//if scrolling stops, return removed child node
$(firstcontainer).append(curChild);
//also prepend the child node to another container element
$(secondcontainer).prepend(curChild2);
Easy, tiger. This page is full of broken links…
@Muzietto, thanks for the heads up, links fixed.
thx! got this trouble