「ボタンを押すと要素が別の場所に移動する」「リストの項目を上下に並び替える」——こういった DOM 操作は、挿入メソッドを既存要素に使うだけで実現できます。
ポイントは、appendChild や append などの挿入メソッドは、既存の要素を渡すと自動的に元の場所から取り除いて移動するという動作をすることです。この記事では移動の仕組みと各メソッドの使い分け、実践的なパターンを体系的に解説します。
「移動」の仕組み:挿入メソッドは既存要素を自動で取り除く
DOM の挿入メソッド(appendChild など)に既にページ上に存在する要素を渡すと、その要素は元の位置から自動的に取り除かれ、指定した場所に挿入されます。つまり、removeChild を呼ばなくても「切り取り&貼り付け」になるのがポイントです。
<div id="boxA"> <p id="target">このpを移動させる</p> </div> <div id="boxB"> <!-- ここに target を移動させたい --> </div>
const target = document.getElementById('target');
const boxB = document.getElementById('boxB');
// appendChild に既存要素を渡すと「移動」になる
boxB.appendChild(target);
// 結果: #target は #boxA から消え、#boxB の末尾に追加される
// → removeChild は不要!
挿入メソッドの種類と使い分け
「どこに挿入するか」によって使うメソッドが変わります。それぞれの違いを整理します。
| メソッド | 挿入位置 | 複数要素 | テキスト直接指定 |
|---|---|---|---|
parent.appendChild(el) |
親の末尾 | ×(1つずつ) | × |
parent.prepend(el) |
親の先頭 | ○ | ○ |
parent.append(el) |
親の末尾 | ○ | ○ |
parent.insertBefore(el, ref) |
ref の直前 | × | × |
ref.before(el) |
ref の直前 | ○ | ○ |
ref.after(el) |
ref の直後 | ○ | ○ |
ref.replaceWith(el) |
ref と置換 | ○ | ○ |
const list = document.getElementById('list');
const itemA = document.getElementById('itemA');
const itemB = document.getElementById('itemB');
// 末尾に移動
list.appendChild(itemA);
list.append(itemA); // 同等(append は複数・文字列も渡せる)
// 先頭に移動
list.prepend(itemA);
// itemB の直前に移動
list.insertBefore(itemA, itemB);
itemB.before(itemA); // 同等
// itemB の直後に移動
itemB.after(itemA);
// itemB と置き換え(itemB は消える)
itemB.replaceWith(itemA);
コピーしたい場合は cloneNode を使う
「元の位置に残しつつ、別の場所にも同じ要素を置く」場合は cloneNode でコピーしてから挿入します。
const original = document.getElementById('template');
// true を渡すと子孫要素も含めてディープコピー
const clone = original.cloneNode(true);
// clone を別の場所に挿入(original はそのまま残る)
document.getElementById('container').appendChild(clone);
// 注意:clone には id が重複する可能性があるため、
// clone した後に id を変更するか、最初から id を付けない設計にする
clone.id = 'template-copy';
cloneNode は要素の HTML 構造と属性をコピーしますが、addEventListener で登録したイベントリスナーはコピーされません。クローン後に改めてイベントを登録する必要があります。(onclick 属性のようなインラインハンドラはコピーされます。)実践:リストの項目を上下に並び替える
「↑」「↓」ボタンでリスト項目を1つずつ移動させる、よく使うUIパターンです。
<ul id="sortableList"> <li>項目 A <button class="move-up">↑</button><button class="move-down">↓</button></li> <li>項目 B <button class="move-up">↑</button><button class="move-down">↓</button></li> <li>項目 C <button class="move-up">↑</button><button class="move-down">↓</button></li> <li>項目 D <button class="move-up">↑</button><button class="move-down">↓</button></li> </ul>
const list = document.getElementById('sortableList');
// イベント委譲でボタンのクリックを一括処理
list.addEventListener('click', (e) => {
const btn = e.target.closest('.move-up, .move-down');
if (!btn) return;
const li = btn.closest('li');
const isUp = btn.classList.contains('move-up');
if (isUp) {
const prev = li.previousElementSibling;
if (prev) list.insertBefore(li, prev); // li を prev の直前に移動
} else {
const next = li.nextElementSibling;
if (next) next.after(li); // next の直後に移動
}
});
previousElementSibling が null なら先頭なので上移動できません。nextElementSibling が null なら末尾なので下移動できません。境界チェックを入れることでリストの外に飛び出すバグを防げます。実践:ドラッグ&ドロップでリストを並び替える
HTML5 の Drag and Drop API を使うと、マウスでドラッグして順番を変えるUIを実装できます。
<ul id="dndList"> <li draggable="true" data-id="1">項目 1</li> <li draggable="true" data-id="2">項目 2</li> <li draggable="true" data-id="3">項目 3</li> <li draggable="true" data-id="4">項目 4</li> </ul>
#dndList li {
padding: 10px 16px;
margin: 4px 0;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
cursor: grab;
transition: opacity 0.2s, background 0.2s;
}
#dndList li.dragging {
opacity: 0.4;
cursor: grabbing;
}
#dndList li.drag-over {
background: #eff6ff;
border-color: #93c5fd;
}
const dndList = document.getElementById('dndList');
let draggingEl = null;
dndList.addEventListener('dragstart', (e) => {
draggingEl = e.target.closest('li');
draggingEl.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
});
dndList.addEventListener('dragend', () => {
draggingEl?.classList.remove('dragging');
dndList.querySelectorAll('.drag-over').forEach((el) => {
el.classList.remove('drag-over');
});
draggingEl = null;
});
dndList.addEventListener('dragover', (e) => {
e.preventDefault(); // ドロップを許可
const target = e.target.closest('li');
if (!target || target === draggingEl) return;
// ドラッグ中の要素がターゲットの上半分にあるか下半分にあるかで挿入位置を決める
const rect = target.getBoundingClientRect();
const midY = rect.top + rect.height / 2;
const isAbove = e.clientY < midY;
dndList.querySelectorAll('.drag-over').forEach((el) => el.classList.remove('drag-over'));
target.classList.add('drag-over');
if (isAbove) {
dndList.insertBefore(draggingEl, target);
} else {
target.after(draggingEl);
}
});
dragover イベントで e.preventDefault() を呼ぶことでドロップ可能になります。これを忘れると drop イベントが発火しません。DocumentFragment で複数要素を一括移動する
複数の要素を移動するとき、1つずつ挿入するとその都度 DOM が更新されてパフォーマンスが落ちます。DocumentFragment にまとめてから1回の挿入で済ませると、リフローが1回に抑えられます。
const source = document.getElementById('source');
const destination = document.getElementById('destination');
// 移動したい要素を選択
const itemsToMove = source.querySelectorAll('.move-target');
// DocumentFragment に一時的に収める
const fragment = document.createDocumentFragment();
itemsToMove.forEach((el) => fragment.appendChild(el)); // source から切り離される
// 1回の操作で destination に挿入(リフローは1回)
destination.appendChild(fragment);
// ※ fragment 自体は DOM に追加されず、中身だけが展開される
DocumentFragment は軽量なコンテナで、appendChild などで別の要素に挿入すると中身だけが展開されフラグメント自体は残りません。レンダリングに影響を与えずに複数ノードをまとめて操作できる点が最大のメリットです。insertAdjacentElement との違い
insertAdjacentElement(position, el) はより細かい挿入位置を文字列で指定できるメソッドです。
const ref = document.getElementById('reference');
const el = document.getElementById('moving');
// ref の開始タグの直前(ref の前の兄弟になる)
ref.insertAdjacentElement('beforebegin', el);
// ref の開始タグの直後(ref の最初の子になる)
ref.insertAdjacentElement('afterbegin', el);
// ref の終了タグの直前(ref の最後の子になる)
ref.insertAdjacentElement('beforeend', el);
// ref の終了タグの直後(ref の後の兄弟になる)
ref.insertAdjacentElement('afterend', el);
// before/after/prepend/append で同等の操作が可能
ref.before(el); // beforebegin と同じ
ref.prepend(el); // afterbegin と同じ
ref.append(el); // beforeend と同じ
ref.after(el); // afterend と同じ
before・after・prepend・append が使えるため、insertAdjacentElement は必須ではありません。ただし位置を文字列で動的に切り替えたい場合は insertAdjacentElement が便利です。よくある質問
appendChild は1つのノードしか受け取れず、テキスト文字列も渡せません。append は複数のノードや文字列を同時に渡せる新しいメソッドです。どちらも既存要素を渡すと「移動」になる動作は同じです。特別な理由がなければ append を使うほうが柔軟です。nextSibling)を変数に保存しておきます。「元に戻す」ときは originalParent.insertBefore(el, originalNextSibling) で元の位置に戻せます。nextSibling が null の場合は末尾だったということなので、appendChild で末尾に追加します。getBoundingClientRect() で移動前後の座標を取得し、CSS の transform: translate() で差分をアニメーションする「FLIP テクニック」が効果的です。要素を実際に移動させてから、逆方向にトランジションすることで自然な動きを実現できます。sort や splice で並び替え、Vue なら reactive な配列を操作してください。id はページ内で一意である必要があります。クローン後に clone.id = "" で id を削除するか、clone.id = "new-unique-id" で別の id を付け直してください。querySelectorAll("[id]") でクローン内の全 id 付き要素を取得して一括でリセットする方法もあります。まとめ
JavaScript での要素移動のポイントをまとめます。
appendChild・append・before・afterなどの挿入メソッドは、既存要素を渡すと自動的に移動になるremoveChildを呼ばなくても「切り取り&貼り付け」ができる- コピーしたい場合は
cloneNode(true)→ 挿入の2ステップで行う - 複数要素を一括移動するときは
DocumentFragmentでリフローを1回に抑える - ドラッグ&ドロップ実装は
dragoverでpreventDefault()を忘れない
要素の動的な追加・削除については【JavaScript】マウスオーバーで要素を動的に追加・削除する実践ガイド、クラスの操作は【JavaScript】classList の使い方完全ガイドもあわせてご覧ください。

