jQuery / Method / .clone() - Copy Selected Elements
jQuery's .clone() creates a new jQuery object containing copies of selected elements, their descendants and text. Cloning alone does not add them to the page; insert them with a method such as .appendTo(). jQuery events and data are excluded by default and controlled separately by arguments.
Structure, moving and event options
In the .clone() documentation, a deep structural copy includes child elements. It does not promise fully independent copies of all events, .data() objects and nested values. The second argument, deepWithDataAndEvents, controls descendant jQuery events/data, not whether the DOM children themselves are copied.
| Expression | Element and descendants | Root events/data | Descendant events/data |
|---|---|---|---|
| .clone() | Copied | Not copied | Not copied |
| .clone(true, false) | Copied | Copied | Not copied |
| .clone(true, true) | Copied | Copied | Copied |
| .clone(true) | Copied | Copied | Copied; second argument follows the first |
Using .appendTo() to insert an existing element into one target moves it from its original position. To retain the original, clone first and insert the copy. They are separate Document Object Model (DOM) elements, so changing the copy's title does not change the original's title.
Complete card example comparing data and clicks
Save all the code as UTF-8 in clone-demo.html and open it in a browser. It loads jQuery 4.0.0 from the official Content Delivery Network (CDN), requiring internet but no installation. All three copy modes, click behavior, shared data and reset were executed with 3.7.1 and 4.0.0.
The original root stores count and kind as jQuery data; its inner Increment button has a directly bound handler. For comparison, the code explicitly resets each copy's count and displayed number to zero. Cloning does not automatically reset counters.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Clone structure, data and events</title>
<style>
body { font: 16px/1.6 sans-serif; margin: 24px; }
.card { padding: 14px; margin: 12px 0; border: 1px solid #8aa; }
.card p { margin: 4px 0; }
button { margin: 4px; padding: 8px; }
</style>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
</head>
<body>
<h1>Clone structure, data and events</h1>
<div class="card" id="source">
<p class="label">Original</p>
<p>Count: <span class="count">0</span></p>
<p>Data: <span class="data">stored</span></p>
<button type="button" class="increment">Increment</button>
</div>
<button type="button" id="plain">Clone structure</button>
<button type="button" id="parent">Clone parent data</button>
<button type="button" id="full">Clone events</button>
<button type="button" id="reset">Reset</button>
<div id="copies"></div>
<p id="result" aria-live="polite"></p>
<script>
$(function () {
const $source = $("#source").data({ count: 0, kind: "stored" });
let serial = 1;
function report() {
$("#result").text(`source=${$source.data("count")} | copies=${$("#copies > .card").length}`);
}
$source.find(".increment").on("click", function () {
const $card = $(this).closest(".card");
const count = $card.data("count") + 1;
$card.data("count", count).find(".count").text(count);
report();
});
function copy(withData, deep) {
const $copy = $source.clone(withData, deep);
$copy.attr("id", "copy-" + serial);
$copy.find(".label").text("Copy " + serial++);
$copy.data("count", 0).find(".count").text(0);
$copy.find(".data").text($copy.data("kind") || "none");
$copy.appendTo("#copies");
report();
}
$("#plain").on("click", () => copy(false, false));
$("#parent").on("click", () => copy(true, false));
$("#full").on("click", () => copy(true, true));
$("#reset").on("click", function () {
$("#copies").empty();
serial = 1;
$source.data("count", 0).find(".count").text(0);
report();
});
report();
});
</script>
</body>
</html>
Initially, only the original exists:
source=0 | copies=0
- Click the original's Increment once to set Count to 1.
- Clone structure copies markup without kind data or the handler. Copy 1 displays Data none, and Increment leaves its count at zero.
- Clone parent data copies root kind, displaying stored, but excludes the descendant button's handler, so it does not increment.
- Clone events includes descendant handlers. Copy 3's Increment raises only that card to 1; the original remains 1.
- Reset removes all copies and resets original count and copy numbering. It does not bind the original handler again, so repeated resets preserve one increment per click.
Before insertion, each root receives a unique ID such as copy-1; inner parts use classes. If cloned descendants have IDs or references such as label for or aria-controls, update both IDs and their references together.
Copy nested objects and arrays explicitly
Objects and arrays inside .data() copied with .clone(true) can remain shared. Run this complete fragment in the developer console to compare shared and independent data on temporary elements that are not inserted into the page.
const $original = $("<div>").data("options", { tags: ["a"] });
const $shared = $original.clone(true);
$shared.data("options").tags.push("b");
console.log($original.data("options").tags.join(", "));
const $independent = $original.clone(true).data("options", {
tags: [...$original.data("options").tags]
});
$independent.data("options").tags.push("c");
console.log($original.data("options").tags.join(", "));
console.log($independent.data("options").tags.join(", "));
a, b
a, b
a, b, c
Adding b through the first copy changes the original to a, b because both reference the same options object. The independent copy gets a new options object and tags array, so adding c leaves the original at a, b. This copies a known structure containing a string array, not arbitrary deeply nested objects.
Form state, event design and troubleshooting
Do not assume current state is copied identically for every form control. The official documentation distinguishes dynamic input state from textarea/select state. Inspect the values you need and consider explicitly assigning selections and notes with .val() after cloning.
Copying every handler is not the only way to make new cards work. Delegating with .on() on a persistent parent can handle newly inserted descendants. Combining direct copied handlers and delegation can execute the same action twice; choose the approach you need and check its result.
If a copy is missing, check insertion and whether the destination exists. For inactive buttons, inspect whether handlers belong to root or descendants and check both arguments. An empty selection clones to an empty set; duplicate IDs can misdirect selectors or labels. Also inspect jQuery loading in Network and errors in Console.




