jQuery / Método / .detach() - Remover elementos e guardá-los para reutilização

No jQuery, .detach() retira os elementos selecionados do modelo de objetos do documento, DOM, preservando seus dados e eventos jQuery. Guarde o objeto jQuery devolvido ou uma referência existente em uma variável para inserir novamente. Isso não é armazenamento permanente no navegador nem no servidor.

Escolher entre detach, remove e empty

.detach() serve para retirar o mesmo elemento temporariamente e reutilizá-lo depois. O elemento e seus filhos saem juntos, mas uma referência aos nós permite reinseri-los. Diferentemente de esconder com CSS, eles não estão mais no documento e uma nova seleção nele não os encontra.

Método O que sai do documento Dados/eventos jQuery das partes removidas Uso comum
.detach() Elementos selecionados e conteúdo Preservados Separação temporária e reutilização
.remove() Elementos selecionados e conteúdo Limpos Descartar elementos
.empty() Filhos e texto dos selecionados Limpos nos filhos removidos Esvaziar mantendo o contêiner

.remove() não destrói necessariamente de imediato um objeto nó ainda referenciado por uma variável JavaScript. Reinseri-lo não restaura automaticamente os dados e eventos jQuery limpos. .empty() mantém o próprio contêiner. Não escolha apenas pelo mesmo efeito visual de desaparecer.

Exemplo completo preservando entrada e eventos

Salve todo o código em UTF-8 como detach-demo.html e abra no navegador. É preciso internet para carregar o arquivo oficial do jQuery 4.0.0 pela rede de distribuição de conteúdo, CDN, sem instalar pacotes. Entrada preservada, mesmo nó e posição, separação/restauração repetidas e comparações no Console foram executadas com 3.7.1 e 4.0.0.

Um nó de comentário anterior ao cartão marca sua posição. Ele permanece ao separar o cartão, permitindo que Restore o devolva ao lugar original. O cartão armazena count por .data() e seu botão filho tem um manipulador direto.

<!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>

Inicialmente o cartão está no documento e não está guardado separadamente no estado destacado:

attached=true | saved=false | count=0
  1. Altere Note para Edited note e pressione uma vez Increment no cartão. Count passa a 1.
  2. Pressione Detach. O cartão some e o resultado vira attached=false | saved=true | count=1. Os dados ficam, mas uma nova consulta de .card no documento encontra zero elementos.
  3. Restore o devolve entre Before e After, mantendo Edited note e Count 1.
  4. Increment eleva Count a 2, mostrando que o manipulador do filho também foi preservado.
  5. Repetir Detach ou Restore não perde nem duplica o cartão porque o código verifica o estado guardado.
  6. Reset funciona mesmo enquanto o cartão está separado. Limpa o antigo com .remove() e cria outro com Keep me e Count 0.

Manter o valor atual do mesmo nó difere de criar um nó com outra string HTML. Restore reinsere o nó guardado; Reset cria outro de propósito. Cada cartão novo recebe o manipulador uma vez, portanto Reset repetido não multiplica os incrementos por clique.

Comparar dados e eventos com remove

Execute este trecho completo no Console das ferramentas de desenvolvimento. Ele cria dois botões em um contêiner temporário, separa um, remove o outro com remove e reinsere ambos. A última linha limpa o contêiner de teste.

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

O botão separado é o mesmo nó e mantém token e seu manipulador: kept clicks é 1. O removido também pode ser inserido pela referência, mas seus dados jQuery são undefined e o manipulador direto foi limpo: removed clicks é 0. A comparação trata de dados e eventos gerenciados pelo jQuery; não garante limpeza de temporizadores externos ou do estado de outras bibliotecas.

Filtro por seletor e cuidados ao restaurar

O seletor de .detach(selector) filtra os membros da seleção já feita. Ele não procura novos filhos dentro deles. O exemplo de Console seleciona A e B e separa apenas B com .take, deixando A no contêiner. As duas últimas linhas limpam nós referenciados e contêiner temporário.

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

Uma nova consulta $(".card") após separar não recupera o cartão. Use a referência guardada e gerencie a posição anterior separadamente, por exemplo com o comentário. .appendTo() insere no final do destino e não lembra posições antigas. Um destino move o nó existente; vários podem criar cópias, então confira a quantidade.

Um evento delegado com .on() no pai depende da propagação por esse pai. Mover o elemento para outro contêiner não transporta os manipuladores delegados do pai anterior. O exemplo usa um manipulador direto no botão filho para mostrar claramente a preservação.

Se não restaurar, veja se a variável foi sobrescrita e se o marcador ou destino continua no documento. Referências mantidas por muito tempo conservam os objetos separados acessíveis. Quando a reutilização acabar, limpe o estado jQuery com .remove() e libere as referências. Cancele temporizadores e assinaturas externas pelas respectivas APIs.