タイトル: 要素のコピー
SEOタイトル: jQuery で要素をコピーする方法(clone / clone(true) でイベント込み複製 / appendTo / 動的フォーム追加 / vanilla JS cloneNode との比較)
| この記事の要点 |
|
jQuery clone() の基本
clone() は jQuery オブジェクトをディープコピーするメソッドです。元の要素を残したまま同じ HTML 構造を複製し、appendTo などで好きな場所に挿入できます。動的なフォーム追加、リスト項目の追加 UI などに頻出する基本機能です。
HTML
上記の をコピーする例です。
jQuery 実装
clone() でコピー、appendTo() で #item_list の末尾に追加しています。これだけで「ボタンを押すたびに input が増える」ような動的フォームを実現できます。
clone() と clone(true) の違い
| 呼び方 | イベントハンドラ | data() | 用途 |
|---|---|---|---|
clone() | コピーしない | コピーしない | HTML 構造のみ複製 |
clone(true) | コピーする | コピーする | イベント込みで複製 |
clone(true, true) | コピー + 子孫も | コピー + 子孫も | 完全な深いコピー |
// 例: クリックイベント付きボタンを複製
$("#origBtn").on("click", function() {
alert("クリックされた!");
});
$("#origBtn").clone().appendTo("body");
// コピー先のボタンを押しても何も起きない
$("#origBtn").clone(true).appendTo("body");
// コピー先のボタンも同じアラートが出る
挿入位置の指定
| メソッド | 挿入位置 |
|---|---|
appendTo(parent) | 親の末尾(子要素として) |
prependTo(parent) | 親の先頭 |
insertAfter(target) | 対象の直後(同階層) |
insertBefore(target) | 対象の直前 |
$("#item").clone().insertAfter("#item"); // 同じ階層、直後
$("#card").clone().prependTo("#list"); // リスト先頭に
$("#row").clone().insertBefore("#footerRow"); // フッター行の前に
ID 重複の罠
HTML の id 属性はドキュメント内で一意であるべきですが、clone() は属性をそのまま複製するため同じ id を持つ要素が複数できてしまいます。
// NG: 2 つの #item ができてしまう
$("#item").clone().appendTo("#item_list");
// OK: id を書き換える
let count = $("#item_list input").length;
$("#item")
.clone()
.attr("id", "item_" + count) // id を一意化
.removeAttr("name") // 必要に応じて
.val("") // 値クリア
.appendTo("#item_list");
// よりベストプラクティス: ID ではなくクラスで設計
$(".item").first().clone().appendTo("#item_list");
動的フォーム追加の完全例
$(function() {
// 行追加
$("#addRow").on("click", function() {
const $row = $("#form-rows .row").first().clone();
$row.find("input").val(""); // 値をクリア
$row.appendTo("#form-rows");
});
// 行削除(イベント委譲)
$("#form-rows").on("click", ".remove", function() {
if ($("#form-rows .row").length > 1) {
$(this).closest(".row").remove();
}
});
});
イベント委譲(on("click", ".remove", ...))を使えば、後から追加された行の削除ボタンにも自動でイベントが効きます。これと clone() を組み合わせるのが動的フォームの王道パターン。
外す系メソッドとの関係
| メソッド | 挙動 |
|---|---|
clone() | 元を残してコピー |
detach() | DOM から外すが内部のデータ / イベントは保持 |
remove() | DOM から削除、データもクリア |
empty() | 子要素のみ削除 |
純粋な JavaScript(vanilla)での書き方
jQuery を使わない場合、Node.cloneNode(true) で同等の処理ができます。モダンブラウザではこれで十分なケースも多い。
function copyItem() {
const orig = document.getElementById("item");
const copy = orig.cloneNode(true); // true で子孫もコピー
copy.removeAttribute("id"); // ID 重複回避
copy.value = "";
document.getElementById("item_list").appendChild(copy);
}
| 項目 | jQuery clone | cloneNode |
|---|---|---|
| 子孫も複製 | 常に深い | 引数 true で深い |
| イベントもコピー | clone(true) で可 | cloneNode は不可(手動で設定) |
| jQuery 依存 | あり | なし(標準 DOM API) |
パフォーマンスの注意
- 巨大な DOM を頻繁に clone するとメモリと描画コストが大きい
- 追加が大量にあるならDocumentFragment を使ってまとめて挿入(vanilla JS の場合)
- 頻繁な DOM 追加削除は仮想 DOM フレームワーク(React / Vue)の方が効率的
FAQ
Q: clone() でイベントが効かない
A: 既定の clone() はイベントをコピーしません。clone(true) を使うか、イベント委譲($(parent).on("click", ".child", ...))で解決します。後者の方がメモリ効率が良いです。
Q: フォームの value がコピーされない
A: ブラウザによっては input の値属性と value プロパティが別扱い。コピー後に .val("") でクリアしたり、.val(orig.val()) で改めて設定するのが安全。
Q: jQuery を使わずに同じことをしたい
A: element.cloneNode(true) + parent.appendChild() で同等です。イベントは addEventListener で都度設定。
関連
- jQuery — DOM 操作を簡潔にするライブラリ
- appendTo / prependTo — 要素の追加
- on — イベントハンドラの設定(委譲も対応)
- cloneNode — 純粋 JS の DOM 複製 API
- DocumentFragment — 一括挿入の高速化