jQuery / Method / .replaceWith() - 선택한 요소를 다른 것으로 바꾸는 메서드
jQuery의 .replaceWith(새 내용)는 선택한 요소 자체를 새 내용으로 교체합니다. 반환값은 새로 들어간 요소가 아니라 제거된 기존 요소의 jQuery 객체입니다. 따라서 교체 뒤 새 요소를 계속 다루려면 별도로 참조를 보관하거나 문서에서 다시 선택해야 합니다.
요소 전체 교체와 내부 내용 변경
.replaceWith()는 DOM(Document Object Model, 문서 객체 모델)의 기존 노드와 그 자식들을 제거하고 그 자리에 새 내용을 넣습니다. $(".entry").replaceWith("<section>New</section>")라면 기존 .entry의 바깥 태그·클래스·자식까지 새 section으로 대체됩니다. 새 section에 .entry를 쓰지 않으면 그 클래스가 자동으로 이어지지 않습니다.
바깥 요소는 유지하고 안쪽 HTML만 바꾸려면 .html()을 사용합니다. .replaceWith()에는 HTML 문자열뿐 아니라 DOM 요소·요소 배열·jQuery 객체, 새 내용을 반환하는 함수도 전달할 수 있습니다. 함수에서는 this로 각 기존 요소를 읽어 필요한 값을 새 요소에 명시적으로 옮길 수 있습니다.
입력값과 이벤트를 관찰하는 전체 예제
전체 코드를 UTF-8로 replace-demo.html에 저장하고 브라우저로 엽니다. 콘텐츠 전송망(CDN)의 공식 jQuery 파일 4.0.0을 읽으므로 인터넷이 필요하며 설치는 없습니다. 아래 교체·입력값·이벤트·초기화와 콘솔 코드를 3.7.1·4.0.0에서 실행했습니다.
두 div.entry를 section.entry로 바꾸되 이름은 .text(), 편집 중인 입력값은 .val()로 새 노드에 옮깁니다. 버튼에는 기존 노드에 직접 등록한 처리기와, 교체되지 않는 #board에 위임한 처리기를 각각 두어 차이를 확인합니다.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Replace whole elements</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%; box-sizing: border-box; }
.entry { padding: 12px; border: 1px solid #aab; margin: 10px 0; }
section.entry { border: 2px solid teal; background: #edf9f6; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<h1>Replace the outer element</h1>
<button type="button" id="replace">Replace both entries</button>
<button type="button" id="reset">Reset</button>
<div id="board">
<div class="entry"><strong class="name">Alpha</strong><input aria-label="Alpha note" value="First note"><button type="button" class="inspect">Inspect</button></div>
<div class="entry"><strong class="name">Beta</strong><input aria-label="Beta note" value="Second note"><button type="button" class="inspect">Inspect</button></div>
</div>
<pre id="result" aria-live="polite"></pre>
<script>
$(function () {
const initial = $("#board").html();
let $old, $returned = null, direct = 0, delegated = 0;
function report() {
const tags = $("#board > .entry").map(function () { return this.tagName.toLowerCase(); }).get();
const connected = $old.filter(function () { return this.isConnected; }).length;
$("#result").text(`live tags=${tags.join(",")}\noriginal connected=${connected}\nreturned original=${$returned ? $returned === $old : "(not run)"}\nold data=${$old.first().data("lesson")}\ndirect=${direct}; delegated=${delegated}`);
}
function initialize() {
$old = $("#board > .entry").data("lesson", "original");
$old.find(".inspect").on("click.directExample", function () { direct++; });
$returned = null; direct = 0; delegated = 0;
$("#replace").prop("disabled", false);
report();
}
$("#board").on("click.replaceLesson", ".inspect", function () { delegated++; report(); });
$("#replace").on("click", function () {
$returned = $old.replaceWith(function () {
const name = $(this).find(".name").text();
const note = $(this).find("input").val();
return $("<section>", { "class": "entry" }).append(
$("<strong>", { "class": "name" }).text(name),
$("<input>", { "aria-label": name + " note" }).val(note),
$("<button>", { type: "button", "class": "inspect" }).text("Inspect")
);
});
$returned.addClass("old-only");
$(this).prop("disabled", true);
report();
});
$("#reset").on("click", function () { $("#board").html(initial); initialize(); });
initialize();
});
</script>
</body>
</html>
처음 출력은 다음과 같습니다. original connected는 최초 요소 중 문서에 연결된 수이며, old data는 그 요소에 저장한 jQuery 데이터입니다.
live tags=div,div
original connected=2
returned original=(not run)
old data=original
direct=0; delegated=0
- 교체 전 Inspect를 한 번 누르면 direct=1; delegated=1입니다. 두 처리기가 모두 실행됩니다.
- Alpha의 입력값을 수정하고 Replace both entries를 누릅니다. live tags는 section,section, original connected는 0, returned original은 true, old data는 undefined가 됩니다. 입력값은 새 input에 명시적으로 복사했으므로 유지됩니다.
- 교체 후 Inspect를 누르면 delegated만 늘어납니다. 새 버튼은 기존 버튼과 다른 노드이며 직접 처리기를 물려받지 않습니다.
- 코드의 $returned.addClass("old-only")는 제거된 노드에 클래스를 붙입니다. 문서 안의 새 section에는 그 클래스가 생기지 않습니다. 바뀐 반환 대상의 의미를 개발자 도구에서도 확인할 수 있습니다.
- 교체 버튼은 한 번 실행한 뒤 비활성화됩니다. Reset은 원래 HTML·입력값·카운터를 복원하고 직접 처리기를 다시 등록합니다. 위임 처리기는 바깥 #board에 한 번만 등록하므로 Reset을 반복해도 중복되지 않습니다.
입력값·데이터·포커스가 교체만으로 자동 이전된다고 가정하지 않습니다. 이 예제는 현재 value만 읽어 옮기며 포커스 복원은 구현하지 않았습니다. 실제 화면에서 사용자가 계속 입력해야 한다면 새 컨트롤의 참조를 확보한 뒤 필요한 위치로 포커스를 옮기는 동작도 별도로 설계합니다.
반환값과 기존 노드 이동 확인하기
새 요소를 계속 사용할 때는 생성한 jQuery 객체를 $new처럼 보관한 뒤 replaceWith에 넘깁니다. 다음 코드를 같은 페이지의 개발자 도구 Console에서 실행합니다. 임시 영역을 만들고 제거하므로 본문 예제의 두 카드는 바꾸지 않습니다. .replaceAll()은 새 내용에서 대상 쪽으로 쓰는 형태이며 반환 집합도 아래처럼 삽입된 요소를 가리킵니다.
const $holder = $('<div><p class="slot">A</p><p class="slot">B</p></div>').appendTo("body");
const $new = $("<section>").text("New");
const $removed = $holder.find(".slot").first().replaceWith($new);
console.log(`removed=${$removed.get(0).tagName.toLowerCase()}; new=${$new.get(0).tagName.toLowerCase()}`);
const $inserted = $("<section>").text("Another").replaceAll($holder.find(".slot"));
console.log(`replaceAll inserted=${$inserted.get(0).tagName.toLowerCase()}; live sections=${$holder.children("section").length}`);
$holder.remove();
removed=p; new=section
replaceAll inserted=section; live sections=2
이미 문서에 있는 노드를 단일 대상의 새 내용으로 넘기면 기존 자리에서 이동합니다. 같은 노드가 새 복사본으로 남는 것은 아닙니다. 다음 코드는 같은 노드인지 직접 비교하고, 부모가 없는 분리 노드에서는 교체 효과가 없다는 점도 확인합니다.
const $holder = $('<div><p class="slot">Placeholder</p><button type="button" class="source">Existing button</button></div>').appendTo("body");
const $source = $holder.find(".source");
const node = $source.get(0);
$holder.find(".slot").replaceWith($source);
console.log(`children=${$holder.children().length}; same node=${$holder.children().get(0) === node}`);
const $orphan = $("<p>Detached without parent</p>");
$orphan.replaceWith("<section>Replacement</section>");
console.log(`orphan tag=${$orphan.get(0).tagName.toLowerCase()}; connected=${$orphan.get(0).isConnected}`);
$holder.remove();
children=1; same node=true
orphan tag=p; connected=false
여러 대상에 같은 HTML 문자열을 넣을 때 id까지 반복하면 중복 id가 생깁니다. 재사용할 서식은 클래스로 구분하고 필요한 id는 각각 고유하게 만듭니다. 여러 대상에 기존 노드 하나를 넘기는 복제·이동 규칙까지 단일 대상 결과로 단정하지 말고, 새 노드가 대상마다 필요하면 함수 안에서 각각 생성하는 방식으로 의도를 명확히 합니다.
교체 후 처리기가 사라지거나 변경이 보이지 않을 때
.replaceWith()는 제거된 노드와 자식에 연결된 jQuery 데이터·이벤트 처리기를 정리합니다. 이는 .on()으로 등록한 기존 처리기를 새 노드에 자동 복사하는 동작이 아닙니다. 교체되는 자식의 반복 동작은 예제의 위임 이벤트 .on()처럼 유지되는 조상에 등록하거나, 새 노드를 만든 뒤 필요한 초기화를 수행합니다. 이 정리 동작을 네이티브 addEventListener 등 모든 외부 상태가 정리된다는 보장으로 확대하지 않습니다.
교체한 뒤 캐시한 기존 선택에 .css()·.addClass()를 연결하면 화면 밖의 노드를 바꿀 수 있습니다. 새 참조 또는 범위를 좁힌 새 선택을 사용하고, 대상이 비어 있거나 부모가 없는지도 확인합니다. 부모 없는 노드를 호출한 반환값만 보고 문서가 바뀌었다고 판단하지 않습니다.
HTML을 받는 메서드에 사용자가 입력한 문자열을 그대로 연결하지 않습니다. 예제는 개발자가 정한 고정 태그를 만들고 이름은 .text(), 입력 내용은 .val()로 전달하므로 <em> 같은 문자열도 태그로 해석하지 않습니다. 이 방법은 일반 텍스트·입력값을 넣는 해당 위치의 처리이며, 임의 HTML 전체를 안전하게 정제하는 방법이라는 뜻은 아닙니다.




