状態を失うことなく DOM 要素を移動する

ウェブ機能: moveBefore()

appendChild() や insertBefore() などの従来の方法を使用して DOM 要素を再親化すると、ブラウザは暗黙的に DOM から要素を削除し、新しい場所に挿入します。この「削除して挿入」オペレーションにより、多くの内部状態がリセットされ、<iframe> 要素が再読み込みされ、CSS アニメーションが再起動され、入力フィールドのフォーカスが失われます。

状態を保持したまま要素を移動するには、moveBefore() API を使用します。このメソッドはアトミックな移動を実行し、削除と挿入のステップを完全にバイパスします。

状態を持つ要素を移動する

moveBefore() は insertBefore() とまったく同じように使用します。移動するノードと、挿入する前の参照ノード(または新しい親の末尾に追加する null)の 2 つの引数が必要です。

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);

カスタム要素(ウェブ コンポーネント)の移動

moveBefore() を使用してカスタム要素を移動する場合、connectedCallback ライフサイクル メソッドと disconnectedCallback ライフサイクル メソッドは呼び出されません。

カスタム要素が移動されたときに特定のロジックを実行する必要がある場合は、カスタム要素の定義内に connectedMoveCallback() メソッドを実装します。

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.
  }
}

代替戦略

moveBefore() はプログレッシブ エンハンスメントであるため、呼び出す前に機能検出を使用し、古いブラウザでは従来の insertBefore() または appendChild() オペレーションにフォールバックする必要があります。

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);
}