The undocumented life of jQuery’s .append()

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.

<html>
    <head>
    <!-- scripts will go here -->
    </head>
    <body>
        <p>original text</p>
        <div></div>
        <div></div>
    </body>
</html>

.append() with cloning

First, let’s take a look at a script that will clone nodes and append them. Demo.

$(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!");
});

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:

<html>
    <head>
    </head>
    <body>
        <p>updated text</p>
        <div>
            <p>original text</p>
        </div>
        <div>
            <p>original text</p>
        </div>
    </body>
</html>

.append() without cloning

Next, let’s take a look at a script that will move nodes instead of clone them. Demo.

$(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!");
});

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:

<html>
    <head>
    </head>
    <body>
        <div>
            <p>updated text</p>
        </div>
        <div></div>
    </body>
</html>

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:

  1. will not remove the input from the DOM
  2. will clone the input and append to the selection

but, appending to exactly 1 target node does the opposite:

  1. will remove the input from the DOM
  2. 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.

12 thoughts on “The undocumented life of jQuery’s .append()

  1. 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);

Leave a Reply

Your email address will not be published. Required fields are marked *