ย้ายองค์ประกอบ DOM โดยไม่สูญเสียสถานะ

ฟีเจอร์บนเว็บ: moveBefore()

เมื่อเปลี่ยนระดับชั้นบน-ล่างขององค์ประกอบ DOM โดยใช้วิธีการแบบเดิม เช่น appendChild() หรือ insertBefore() เบราว์เซอร์จะนำองค์ประกอบออกจาก DOM โดยนัย แล้วแทรกลงในตำแหน่งใหม่ การดำเนินการ "นำออกและแทรก" นี้จะรีเซ็ตสถานะภายในหลายสถานะ ซึ่งทำให้องค์ประกอบ <iframe> โหลดซ้ำ ภาพเคลื่อนไหว CSS รีสตาร์ท และช่องป้อนข้อมูลสูญเสียโฟกัส

หากต้องการย้ายองค์ประกอบโดยคงสถานะไว้ ให้ใช้ moveBefore() API วิธีนี้จะดำเนินการย้ายแบบอะตอมมิก โดยข้ามขั้นตอนการนำออกและการแทรกไปเลย

การย้ายองค์ประกอบที่มีสถานะ

ใช้ moveBefore() ในลักษณะเดียวกับที่คุณใช้ insertBefore() โดยต้องมีอาร์กิวเมนต์ 2 รายการ ได้แก่ โหนดที่จะย้าย และโหนดอ้างอิงที่จะแทรกก่อน (หรือ null เพื่อต่อท้ายโหนดหลักใหม่)

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