jQuery / Método / .clone() - Copiar elementos selecionados

No jQuery, .clone() cria um novo objeto jQuery com cópias dos elementos selecionados, seus descendentes e texto. Clonar não os inclui na página: insira com .appendTo(), por exemplo. Eventos e dados jQuery são excluídos por padrão e controlados por argumentos separados.

Estrutura, movimentação e opções de eventos

Na documentação de .clone(), uma cópia estrutural profunda inclui os filhos. Não garante independência completa de eventos, objetos .data() e valores aninhados. O segundo argumento deepWithDataAndEvents controla eventos/dados dos descendentes, não a cópia dos filhos DOM.

Expressão Elemento e descendentes Eventos/dados da raiz Eventos/dados dos descendentes
.clone() Copiados Não copiados Não copiados
.clone(true, false) Copiados Copiados Não copiados
.clone(true, true) Copiados Copiados Copiados
.clone(true) Copiados Copiados Copiados; o segundo argumento segue o primeiro

Inserir um elemento existente em um único destino com .appendTo() o move. Para manter o original, clone antes. São elementos separados do modelo de objetos do documento, DOM; mudar o título da cópia não muda o original.

Exemplo completo de dados e cliques em cartões

Salve todo o código em UTF-8 como clone-demo.html e abra no navegador. jQuery 4.0.0 vem da rede oficial de distribuição de conteúdo, CDN; exige internet, sem instalação. Os três modos, cliques, dados compartilhados e Reset foram executados com 3.7.1 e 4.0.0.

A raiz original guarda count e kind; o botão interno Increment tem manipulador direto. O código zera explicitamente o contador e o número exibido de cada cópia. Clonar não reinicia contadores automaticamente.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Clone structure, data and events</title>
  <style>
    body { font: 16px/1.6 sans-serif; margin: 24px; }
    .card { padding: 14px; margin: 12px 0; border: 1px solid #8aa; }
    .card p { margin: 4px 0; }
    button { margin: 4px; padding: 8px; }
  </style>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
</head>
<body>
  <h1>Clone structure, data and events</h1>
  <div class="card" id="source">
    <p class="label">Original</p>
    <p>Count: <span class="count">0</span></p>
    <p>Data: <span class="data">stored</span></p>
    <button type="button" class="increment">Increment</button>
  </div>
  <button type="button" id="plain">Clone structure</button>
  <button type="button" id="parent">Clone parent data</button>
  <button type="button" id="full">Clone events</button>
  <button type="button" id="reset">Reset</button>
  <div id="copies"></div>
  <p id="result" aria-live="polite"></p>
  <script>
    $(function () {
      const $source = $("#source").data({ count: 0, kind: "stored" });
      let serial = 1;
      function report() {
        $("#result").text(`source=${$source.data("count")} | copies=${$("#copies > .card").length}`);
      }
      $source.find(".increment").on("click", function () {
        const $card = $(this).closest(".card");
        const count = $card.data("count") + 1;
        $card.data("count", count).find(".count").text(count);
        report();
      });
      function copy(withData, deep) {
        const $copy = $source.clone(withData, deep);
        $copy.attr("id", "copy-" + serial);
        $copy.find(".label").text("Copy " + serial++);
        $copy.data("count", 0).find(".count").text(0);
        $copy.find(".data").text($copy.data("kind") || "none");
        $copy.appendTo("#copies");
        report();
      }
      $("#plain").on("click", () => copy(false, false));
      $("#parent").on("click", () => copy(true, false));
      $("#full").on("click", () => copy(true, true));
      $("#reset").on("click", function () {
        $("#copies").empty();
        serial = 1;
        $source.data("count", 0).find(".count").text(0);
        report();
      });
      report();
    });
  </script>
</body>
</html>

Inicialmente, só existe o original:

source=0 | copies=0
  1. Use Increment do original uma vez para obter Count 1.
  2. Clone structure copia estrutura sem kind nem manipulador. Copy 1 mostra Data none e seu botão mantém zero.
  3. Clone parent data copia kind e mostra stored, mas não o manipulador do botão descendente; não incrementa.
  4. Clone events inclui manipuladores descendentes. Copy 3 passa a 1 e o original permanece 1.
  5. Reset remove as cópias e reinicia contador original e numeração. Não registra novamente o manipulador original; cada clique continua incrementando uma vez após repetir Reset.

Antes de inserir, a raiz recebe ID único como copy-1; partes internas usam classes. Se descendentes têm IDs ou referências label for e aria-controls, atualize identificadores e referências juntos.

Copiar objetos e arrays aninhados explicitamente

Objetos e arrays em .data() copiados por .clone(true) podem permanecer compartilhados. Execute este trecho completo no console de desenvolvimento; ele usa elementos temporários sem inserção.

const $original = $("<div>").data("options", { tags: ["a"] });
const $shared = $original.clone(true);
$shared.data("options").tags.push("b");
console.log($original.data("options").tags.join(", "));
const $independent = $original.clone(true).data("options", {
  tags: [...$original.data("options").tags]
});
$independent.data("options").tags.push("c");
console.log($original.data("options").tags.join(", "));
console.log($independent.data("options").tags.join(", "));
a, b
a, b
a, b, c

Acrescentar b pela primeira cópia também muda o original para a, b porque ambos usam o mesmo options. A cópia independente recebe options e tags novos; incluir c deixa o original em a, b. Copia-se uma estrutura conhecida com array de strings, não qualquer objeto profundamente aninhado.

Estado de formulários, eventos e problemas

Não suponha que todo controle copia igualmente seu estado atual. A documentação distingue input de textarea/select. Confira os valores necessários e considere definir seleções e notas explicitamente com .val() após clonar.

Copiar todos os manipuladores não é a única opção para novos cartões. A delegação com .on() em um pai persistente pode tratar descendentes novos. Combinar manipuladores diretos copiados e delegação pode duplicar ações; escolha e confira a abordagem necessária.

Se a cópia não aparecer, examine inserção e destino. Para botões inativos, confira se o manipulador estava na raiz ou nos descendentes e os dois argumentos. Conjunto vazio produz cópia vazia; IDs duplicados podem desviar seletores e rótulos. Examine também Network e Console.