Khi gán lại phần tử mẹ cho các phần tử DOM bằng các phương thức truyền thống như appendChild() hoặc insertBefore(), trình duyệt sẽ ngầm xoá phần tử đó khỏi DOM rồi chèn phần tử đó vào vị trí mới. Thao tác "xoá và chèn" này đặt lại nhiều trạng thái nội bộ, khiến các phần tử <iframe> tải lại, ảnh động CSS khởi động lại và các trường nhập dữ liệu mất tiêu điểm.
Để di chuyển một phần tử trong khi vẫn giữ nguyên trạng thái của phần tử đó, hãy sử dụng API moveBefore(). Phương thức này thực hiện một thao tác di chuyển nguyên tử, bỏ qua hoàn toàn các bước xoá và chèn.
Di chuyển một phần tử có trạng thái
Sử dụng moveBefore() giống hệt như cách bạn sử dụng insertBefore(). Phương thức này yêu cầu hai đối số: nút cần di chuyển và nút tham chiếu để chèn trước (hoặc null để thêm vào cuối phần tử mẹ mới).
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);
Di chuyển các phần tử tuỳ chỉnh (Thành phần web)
Nếu bạn di chuyển các phần tử tuỳ chỉnh bằng moveBefore(), thì các phương thức vòng đời connectedCallback và disconnectedCallback của các phần tử đó sẽ không được kích hoạt.
Nếu phần tử tuỳ chỉnh của bạn cần thực hiện logic cụ thể khi di chuyển, hãy triển khai phương thức connectedMoveCallback() bên trong định nghĩa phần tử tuỳ chỉnh.
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.
}
}
Chiến lược dự phòng
Vì moveBefore() là một cải tiến tăng dần, nên bạn PHẢI sử dụng tính năng phát hiện tính năng trước khi gọi, quay lại các thao tác insertBefore() hoặc appendChild() truyền thống cho các trình duyệt cũ.
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);
}