Mover o elemento DOM sem perder o estado

Recursos da Web: moveBefore()

Ao reatribuir elementos DOM usando métodos tradicionais, como appendChild() ou insertBefore(), o navegador remove implicitamente o elemento do DOM e o insere no novo local. Essa operação de "remover e inserir" redefine muitos estados internos, fazendo com que os elementos <iframe> sejam recarregados, as animações CSS sejam reiniciadas e os campos de entrada percam o foco.

Para mover um elemento preservando o estado dele, use a API moveBefore(). Esse método realiza uma movimentação atômica, ignorando completamente as etapas de remoção e inserção.

Como mover um elemento com estado

Use moveBefore() exatamente como você usaria insertBefore(). Ele requer dois argumentos: o nó a ser movido e um nó de referência para inserir antes (ou null para anexar ao final do novo pai).

const newParent = document.getElementById('new-parent');
const elementWithState = document.getElementById('iframe-or-focused-input');

// MANDATORY: Use moveBefore to preserve state. 
// Passing null as the second argument appends the element to the end of newParent.
newParent.moveBefore(elementWithState, null);

Como mover elementos personalizados (componentes da Web)

Se você estiver movendo elementos personalizados usando moveBefore(), os métodos de ciclo de vida connectedCallback e disconnectedCallback não serão acionados.

Se o elemento personalizado precisar executar uma lógica específica quando for movido, implemente o método connectedMoveCallback() na definição do elemento personalizado.

class MyCustomElement extends HTMLElement {
  connectedCallback() {
    // Runs on initial insertion.
  }
  
  connectedMoveCallback() {
    // Runs when the element is moved via moveBefore().
    // Use this to update state that depends on the new DOM location.
  }
}

Estratégias de fallback

Como moveBefore() é um aprimoramento progressivo, você PRECISA usar a detecção de recursos antes de chamá-la, voltando para as operações tradicionais de insertBefore() ou appendChild() em navegadores mais antigos.

const targetParent = document.getElementById('target-container');
const nodeToMove = document.getElementById('moving-element');

// Check if moveBefore is supported on the Element prototype
if ('moveBefore' in Element.prototype) {
  targetParent.moveBefore(nodeToMove, null);
} else {
  // Fallback: traditional move. 
  // Note: This WILL reset <iframe>, animation, and focus state in unsupported browsers.
  targetParent.insertBefore(nodeToMove, null);
}