使用 moveBefore() 在 DOM 更改期间保留状态

我们很高兴地宣布,Chrome 133 版中推出了新的 moveBefore() DOM API,可让您更轻松地在 DOM 中移动元素,而不会丢失状态。请继续阅读,了解如何在项目中使用它!

在 DOM 更改期间丢失状态

您是否使用 appendChild() API 将新元素插入 DOM?许多人都有过这样的疑问,但您是否曾尝试使用 DOM 中已有的元素调用它(或 insertBefore(),或者任何其他插入 API)?如果是这样,您可能没有意识到,此操作会先从旧父元素中移除元素,然后将其重新插入新父元素,从而静默运行。这是因为自 1998 年引入第一个 DOM 标准草稿以来,文档对象模型就只有移除和插入基元。每当您认为自己在 DOM 中将某个内容从一个位置“移动”到另一个位置时,实际上是在后台移除和插入

事实上,“移动”实际上是“移除并插入”,这通常不会影响用户体验。例如,在 DOM 中“移动”<p> 时,这两项操作不会产生干扰性副作用,但当移动包含重要状态的复杂节点(例如 <iframe> 元素、全屏元素、CSS 动画等)时,隐式“移除”操作会重置所有类型的状态。

这可能会产生令人惊讶的破坏性副作用

您可以通过在 DOM 树中尝试移动,在我们的状态保留演示网站中查看会重置哪种状态。以下示例展示了从一个父容器将元素移至另一个父容器时,CSS 动画和 <iframe> 状态的重新设置。

这一限制可能会导致构建动态用户体验变得困难甚至不可能。当应用状态神秘地重置时,用户会感到沮丧和困惑,而 JavaScript 框架作者则要承担这方面的重担,他们需要花费无数小时围绕此问题重新构建前端代码、编写 MorphDOM 等复杂库,或者处理突出显示他们无法解决的问题的 bug 报告。

新的 moveBefore() API

我们决定通过向 DOM 添加新的基元操作来解决此问题。它被恰当地称为“move”基元,并通过新的 moveBefore() DOM API 向开发者公开。

moveBefore() 采用与 insertBefore() 相同的参数,但与在节点已附加到 DOM 时移除并重新插入节点不同,这个新 API 会原子地移动目标节点到新的父节点,而不会重置大多数状态。这样一来,JavaScript 开发者终于可以使用可移动的动画、iframe、全屏元素等构建动态体验了。您可以通过启用 chrome://flags/#atomic-move 实验性标志并访问我们的演示网站,或在 Chrome 133 版于 2025 年 2 月 4 日发布后使用该版本,亲自试用此功能。

借助这个新基元,JavaScript 作者可以实现以下行为:

  • 在用户浏览网站时保留视频的播放状态(无论视频是通过 <video> 还是 <iframe> 元素提供)。
  • 在 DOM 中移动用户输入字段时,保留其焦点。
  • 允许在 DOM 中添加或移除新内容时顺利完成动画。
  • 更高保真度的变形算法,用于将现有 DOM 与新内容进行协调。
  • 让模态对话框、弹出式窗口和全屏元素保持打开状态。

我们正在努力将此 API 引入到其他浏览器的 Web 平台,很高兴能够尽快将其交到开发者手中,满足开发者多年来的请求,并填补 Web 平台中的重要空白。


一如既往,您可以通过 Twitter 或下方的评论与我们分享您的想法,并通过 crbug.com/new 提交 bug。