jQuery / Method / .detach() - 선택한 요소를 문서에서 제거하고 저장하는 메서드
jQuery의 .detach()는 선택한 요소를 DOM(Document Object Model, 문서 객체 모델)에서 분리하면서 해당 요소에 연결된 jQuery 데이터와 이벤트를 유지합니다. 나중에 다시 넣으려면 반환된 jQuery 객체나 기존 참조를 변수에 보관해야 합니다. 브라우저 저장소나 서버에 영구 저장하는 기능은 아닙니다.
detach·remove·empty를 선택하는 기준
.detach()는 같은 요소를 잠시 빼 두었다가 다시 사용할 때 적합합니다. 요소와 그 안의 자식이 함께 문서에서 빠지지만, 분리한 노드를 변수로 참조하면 다시 삽입할 수 있습니다. CSS로 숨기는 것과 달리 문서 안에 존재하지 않으므로 문서를 새로 검색하는 선택자는 찾지 못합니다.
| 메서드 | 문서에서 제거하는 대상 | 제거한 부분의 jQuery 데이터·이벤트 | 주요 용도 |
|---|---|---|---|
| .detach() | 선택한 요소와 그 내용 | 유지 | 분리 후 재사용 |
| .remove() | 선택한 요소와 그 내용 | 정리 | 폐기할 요소 제거 |
| .empty() | 선택한 요소의 자식과 텍스트 | 제거하는 자식 쪽은 정리 | 컨테이너를 남기고 내용 비우기 |
.remove()도 JavaScript 변수가 이미 참조하는 노드 객체 자체를 무조건 즉시 없애는 것은 아닙니다. 다시 삽입할 수 있더라도 jQuery가 정리한 이벤트와 데이터가 자동 복구되지는 않습니다. .empty()는 컨테이너 자체를 분리하지 않습니다. 이 차이를 ‘화면에서 사라진다’라는 결과만으로 판단하지 않습니다.
입력값과 이벤트를 유지하는 전체 실습
코드를 전부 UTF-8의 detach-demo.html로 저장하고 브라우저에서 엽니다. 공식 jQuery 파일 4.0.0을 CDN(Content Delivery Network, 콘텐츠 전송 네트워크)에서 읽을 인터넷 연결이 필요하며 별도 설치는 없습니다. 입력값 유지, 원래 노드·위치 복원, 반복 분리·초기화와 아래 콘솔 비교를 3.7.1과 4.0.0에서 실행했습니다.
카드 앞에 주석 노드를 위치 표시자로 둡니다. 카드를 빼도 이 표시자는 남아 있으므로 Restore는 원래 자리로 돌려놓습니다. 카드에는 .data()의 count와 자식 버튼의 직접 연결 이벤트가 있습니다.
<!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>
처음에는 카드가 문서에 있고 별도로 보관한 상태가 아닙니다.
attached=true | saved=false | count=0
- Note 입력값을 Edited note로 바꾸고 카드의 Increment를 한 번 누릅니다. Count는 1입니다.
- Detach를 누르면 카드가 사라지고 결과는
attached=false | saved=true | count=1입니다. 데이터는 남아 있지만 문서 안의 .card를 새로 검색하면 0개입니다. - Restore를 누르면 Before와 After 문단 사이로 돌아옵니다. 입력값은 Edited note, Count는 1입니다.
- Increment를 다시 누르면 Count가 2가 됩니다. 자식 버튼의 이벤트도 유지된 것입니다.
- Detach나 Restore를 연속해서 눌러도 카드를 잃거나 복제하지 않습니다. 코드가 보관 상태를 검사하기 때문입니다.
- 카드가 분리된 상태에서도 Reset을 누를 수 있습니다. 이 버튼은 기존 카드를 .remove()로 정리하고 새 카드를 만들어 입력값 Keep me와 Count 0으로 초기화합니다.
같은 노드의 현재 입력값을 유지하는 것과 새 HTML 문자열로 노드를 만드는 것은 다릅니다. Restore는 저장한 노드를 다시 넣으며, Reset은 의도적으로 새 노드를 만듭니다. 새 카드마다 이벤트를 한 번 연결하므로 초기화를 반복해도 클릭 한 번에 여러 번 증가하지 않습니다.
remove와 비교해 데이터·이벤트 확인하기
개발자 도구의 Console에서 다음 완전한 부분 코드를 실행합니다. 임시 컨테이너에 버튼 두 개를 만들고, 하나는 detach, 다른 하나는 remove한 뒤 같은 컨테이너에 다시 넣습니다. 마지막 줄은 실습용 컨테이너를 정리합니다.
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
detach한 버튼은 원래 노드와 같고 token 데이터와 클릭 이벤트가 유지되어 kept clicks가 1입니다. remove한 버튼도 변수가 참조하므로 다시 넣을 수 있지만, jQuery 데이터는 undefined이고 기존 직접 이벤트가 정리되어 removed clicks는 0입니다. 이 비교는 jQuery로 관리하는 데이터·이벤트에 관한 것이며 외부 타이머나 다른 라이브러리의 상태까지 정리된다고 일반화하지 않습니다.
선택자 필터와 복원할 때 주의할 점
.detach(selector)의 선택자는 이미 선택한 요소 집합 중 분리할 요소를 거릅니다. 그 요소 안에서 자식을 새로 찾는 인수가 아닙니다. 다음 콘솔 예제는 A와 B를 선택한 후 .take인 B만 문서에서 분리하므로 컨테이너에는 A가 남습니다. 마지막 두 줄은 보관한 노드와 임시 컨테이너를 정리합니다.
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
분리 후 다시 $(".card")를 검색해 복원하려 하면 대상이 없습니다. 변수에 남긴 참조를 사용합니다. 원래 자리로 돌아가야 한다면 예제의 주석 표시자처럼 위치를 따로 관리해야 합니다. .appendTo()는 대상의 끝에 넣는 메서드이므로 과거 위치를 기억해 주지 않습니다. 대상이 하나면 기존 노드를 이동하고, 여러 대상이면 복사본이 생길 수 있어 대상 개수도 확인합니다.
상위 컨테이너에 .on()으로 위임한 이벤트는 그 상위 요소에서 발생하는 이벤트 전파를 이용합니다. 다른 컨테이너로 옮긴 요소가 예전 상위 요소의 위임 이벤트까지 자동으로 가져가는 것은 아닙니다. 위 실습은 보존을 명확히 확인하기 위해 카드 자식에 직접 연결한 이벤트를 사용합니다.
복원되지 않으면 보관 변수가 덮어써지지 않았는지, 위치 표시자나 삽입 대상이 아직 문서에 있는지 확인합니다. 분리한 요소를 장기간 변수에 보관하면 그 객체도 계속 참조하므로 필요가 끝나면 .remove()로 jQuery 상태를 정리하고 참조도 해제합니다. 관련 타이머·외부 구독은 해당 기능의 해제 방법을 별도로 적용합니다.




