jQuery / Method / .detach() - Remove and Keep Elements for Reuse
jQuery's .detach() removes selected elements from the Document Object Model (DOM) while retaining their associated jQuery data and events. Keep the returned jQuery object or an existing reference in a variable if you intend to insert them again. This is not permanent storage in a browser database or on a server.
Choose between detach, remove and empty
.detach() suits an element that will temporarily leave the document and later be reused. The element and its children leave together, but a variable referencing the detached nodes lets you insert them again. Unlike CSS hiding, detachment removes them from the document, so a fresh document selector cannot find them.
| Method | What leaves the document? | jQuery data/events in removed parts | Typical use |
|---|---|---|---|
| .detach() | Selected elements and their contents | Retained | Temporary removal and reuse |
| .remove() | Selected elements and their contents | Cleaned up | Discarding elements |
| .empty() | Children and text of selected elements | Cleaned up for removed children | Clear a container but keep it |
.remove() does not necessarily destroy a node object immediately when a JavaScript variable still references it. Reinserting that node does not automatically restore the jQuery data and events that were cleaned up. .empty() leaves the container itself in place. Do not choose solely by the shared visual outcome of something disappearing.
Complete example retaining input and events
Save the entire code as UTF-8 in detach-demo.html and open it in a browser. Internet is required to load the official jQuery file, version 4.0.0, from its content delivery network (CDN); no package installation is needed. Input retention, same-node and position restoration, repeated detachment/reset and the console comparisons below were executed with 3.7.1 and 4.0.0.
A comment node marks the position before the card. It stays in place during detachment, allowing Restore to return the card to its original position. The card stores count with .data() and has a directly bound event on its child button.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Detach and restore an editable card</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
button, input { font: inherit; margin: 4px; max-width: 100%; }
.card { border: 2px solid teal; padding: 16px; margin: 12px 0; }
</style>
</head>
<body>
<h1>Detach and restore a card</h1>
<button type="button" id="detach">Detach</button>
<button type="button" id="restore">Restore</button>
<button type="button" id="reset">Reset</button>
<div id="slot">
<p>Before the card</p>
<p id="after">After the card</p>
</div>
<p id="result" aria-live="polite"></p>
<script>
$(function () {
const anchor = document.createComment("card position");
$("#after").before(anchor);
let $saved = null;
function makeCard() {
const $card = $('<section class="card"><label>Note <input value="Keep me"></label><p>Count: <span class="count">0</span></p><button type="button" class="increment">Increment</button></section>');
$card.data("count", 0);
$card.find(".increment").on("click", function () {
const count = $card.data("count") + 1;
$card.data("count", count).find(".count").text(count);
report();
});
return $card;
}
let $card = makeCard().insertAfter(anchor);
function report() {
const attached = document.contains($card[0]);
$("#result").text(`attached=${attached} | saved=${$saved !== null} | count=${$card.data("count")}`);
}
$("#detach").on("click", function () {
if ($saved !== null) return;
$saved = $card.detach();
report();
});
$("#restore").on("click", function () {
if ($saved === null) return;
$saved.insertAfter(anchor);
$saved = null;
report();
});
$("#reset").on("click", function () {
$card.remove();
$saved = null;
$card = makeCard().insertAfter(anchor);
report();
});
report();
});
</script>
</body>
</html>
Initially, the card is attached and no separate saved state is active:
attached=true | saved=false | count=0
- Change Note to Edited note and press the card's Increment once. Count becomes 1.
- Press Detach. The card disappears and the result becomes
attached=false | saved=true | count=1. Data remains, but a fresh document query for .card finds zero elements. - Press Restore. The card returns between the Before and After paragraphs, with Edited note and Count 1.
- Press Increment again. Count becomes 2, demonstrating that the child's click handler remains.
- Repeated Detach or Restore does not lose or duplicate the card because the code checks its saved state.
- You can press Reset even while detached. It cleans up the old card with .remove() and creates a new card with Keep me and Count 0.
Retaining an input's current value on the same node differs from creating a node from a new HTML string. Restore reinserts the saved node; Reset intentionally creates another. Each new card receives its handler once, so repeating Reset does not multiply increments per click.
Compare retained data and events with remove
Run the following complete fragment in the developer tools Console. It creates two buttons in a temporary container, detaches one, removes the other, then inserts both again. Its last line cleans up the temporary container.
const $host = $("<div>").appendTo("body");
let keptClicks = 0;
let removedClicks = 0;
const $keep = $("<button>Keep</button>").data("token", "kept")
.on("click", () => keptClicks++).appendTo($host);
const $gone = $("<button>Remove</button>").data("token", "gone")
.on("click", () => removedClicks++).appendTo($host);
const originalNode = $keep[0];
const $saved = $keep.detach();
console.log(`detached=${!document.contains($saved[0])} | data=${$saved.data("token")}`);
$saved.appendTo($host).trigger("click");
const $removed = $gone.remove();
$removed.appendTo($host).trigger("click");
console.log(`same node=${$saved[0] === originalNode} | kept clicks=${keptClicks}`);
console.log(`removed data=${typeof $removed.data("token")} | removed clicks=${removedClicks}`);
$host.remove();
detached=true | data=kept
same node=true | kept clicks=1
removed data=undefined | removed clicks=0
The detached button is the same node and retains token and its click handler, giving kept clicks=1. The removed button can also be inserted because a variable references it, but jQuery data is undefined and its old direct handler was cleaned up, giving removed clicks=0. This comparison concerns jQuery-managed data and events; it does not imply cleanup of outside timers or another library's state.
Selector filtering and restoration pitfalls
The selector in .detach(selector) filters members of the selection already made. It does not search inside them for new children. This console example selects A and B, then detaches only B with class .take; A remains in the container. The final two lines clean up the referenced nodes and temporary container.
const $list = $('<div><p class="keep">A</p><p class="take">B</p></div>').appendTo("body");
const $items = $list.children("p");
$items.detach(".take");
console.log(`remaining=${$list.children("p").text()}`);
$items.remove();
$list.remove();
remaining=A
A fresh $(".card") query after detachment cannot recover the card. Use the saved reference. Manage the old position separately, for instance with the comment marker above. .appendTo() inserts at the target's end; it does not remember an earlier position. One target moves the existing node, while multiple targets may create copies, so check target count too.
An event delegated through .on() on a parent relies on events bubbling through that parent. Moving an element elsewhere does not transfer its former parent's delegated handlers. This example uses a direct handler on a card child to make preservation clear.
If restoration fails, check that the saved variable has not been overwritten and that the marker or insertion target is still in the document. Long-lived references retain detached objects. When reuse is finished, clean up jQuery state with .remove() and release references. Cancel related timers and external subscriptions through their own APIs.




