Chrome 133 (将于 2 月 4 日稳定)中新增了一种在 DOM 中移动元素的新方法:Node.prototype.moveBefore
。虽然方法很小,但意义重大,因为它在移动元素时会保留元素的状态!例如:
iframe 保持加载状态
活动元素依然是焦点
弹出窗口、全屏、模式对话框保持打开状态
CSS 过渡和动画继续
…
旧与新
移动物体的经典方式是使用。这样做时, “移动”Node.prototype.insertBefore
的节点将重新加载其初始状态。
document.querySelector('#classic').addEventListener('click', () => { const $newSibling = getRandomElementInBody(); const $iframe = document.querySelector('iframe'); document.body.removeChild($iframe); document.body.insertBefore($iframe, $newSibling); });
即使你省略了对 的调用removeChild
,insertBefore
仍会自行执行该操作 - 即使目标仍与父级相连。我将其保留下来是为了让幕后发生的事情更加清晰。
移动元素的新方法是使用moveBefore
。语法方面,该方法是模仿的,insertBefore
因此您可以轻松地交换内容。
document.querySelector('#classic').addEventListener('click', () => { const $newSibling = getRandomElementInBody(); const $iframe = document.querySelector('iframe'); document.body.moveBefore($iframe, $newSibling); });
moveBefore
移动元素的状态得以保留。
演示
这是一个包含两种方法的演示。
HTML
<h1>DOM State-Preserving Move <em>(<code>Node.prototype.moveBefore</code>)</em></h1> <p>With <code>Node.prototype.moveBefore</code> you can move elements around a DOM tree, without resetting the element's state.</p> <p>When moving – instead of using the classic way of removing+inserting – the state of a node is preserved. For example:</p> <ul> <li>Iframes remain loaded</li> <li>Active element remains focus</li> <li>Popovers, fullscreen, modal dialogs remain open</li> <li>CSS transitions and animations carry on</li> </ul> <h2>Demo</h2> <h3>Instructions</h3> <ol> <li>Start the video in the YouTube Embed below (iframe)</li> <li>Hit one of the move buttons</li> <li>Note how the iframe keeps playing when using <code>moveBefore</code></li> </ol> <iframe width="560" height="315" src="https://www.youtube.com/embed/e7BkmF8CJpQ?si=-O_hK4VvqNfVof1L" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> <h3>Code</h3> <pre><code>document.querySelector('#movebefore').addEventListener('click', () => { const $newSibling = getRandomElementInBody(); const $iframe = document.querySelector('iframe'); <b>document.body.moveBefore($iframe, $newSibling);</b> });</code></pre> <div class="buttons"> <button id="classic">Move <code>iframe</code> around the classic way</button> <button id="movebefore">Move <code>iframe</code> around using <code>moveBefore</code></button> </div> <footer><p>Demo for <a href="http://brm.us/movebefore" target="_top">http://brm.us/movebefore</a></p></footer> <div class="warning"> <p>You browser does not support <code>ParentNode.prototype.moveBefore</code>. Try Chrome 133+.</p> </div>
CSS
@layer reset, layout, demo; @layer demo { iframe { aspect-ratio: 16 / 9; } .buttons { position: fixed; top: 0; right: 0; display: flex; flex-direction: column; gap: 0.5em; padding: 1em; } button { font-size: 1.25em; padding: 0.6em 0.7em; background: aliceblue; border-radius: 0.5rem; cursor: pointer; &:hover, &:focus { background: lightblue; } } .warning { position: fixed; bottom: 0; left: 1em; right: 1em; } } @layer layout { @layer general { html { font-family: sans-serif; } body { padding: 3rem 0 10rem; width: 90%; max-width: 50em; margin: 0 auto; } h2 { margin-top: 2em; } p, ul { margin-block: 1em; } a { color: #0000aa; text-decoration: none; &:hover { color: blue; } } footer { text-align: center; font-style: italic; margin: 4em 0; } iframe { display: block; width: 100%; height: auto; margin: 1em auto; } button, input, select { font-family: inherit; } } @layer code { script[visible], style[visible], pre { display: block; white-space: pre; border: 1px solid #dedede; padding: 1em; background: #fafafa; font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; overflow-x: auto; border-left: 0.4em solid cornflowerblue; tab-size: 2; color: #1a1a1a; line-height: 1.6; } code:not(pre code) /*, output:not(code:has(output) output) */ { background: #f7f7f7; border: 1px solid rgb(0 0 0 / 0.2); padding: 0.1rem 0.3rem; margin: 0.1rem 0; border-radius: 0.2rem; display: inline-block; } } @layer warning { .warning { display: none; box-sizing: border-box; padding: 1em; margin: 1em 0; border: 1px solid #ccc; background: rgba(255 255 205 / 0.8); } .warning > :first-child { margin-top: 0; } .warning > :last-child { margin-bottom: 0; } .warning a { color: blue; } .warning--info { border: 1px solid #123456; background: rgb(205 230 255 / 0.8); } .warning--alarm { border: 1px solid red; background: #ff000010; } } } @layer reset { * { box-sizing: border-box; } html, body { margin: 0; padding: 0; } }
JS
const randomBetween = (min, max) => { return Math.floor(Math.random() * (max - min + 1) + min); } const getRandomElementInBody = () => { const $candidates = document.querySelectorAll('body > *:not(.buttons, iframe, .warning)'); return $candidates[randomBetween(0, $candidates.length)]; } document.querySelector('#movebefore').addEventListener('click', () => { const $newSibling = getRandomElementInBody(); const $iframe = document.querySelector('iframe'); document.body.moveBefore($iframe, $newSibling); }); document.querySelector('#classic').addEventListener('click', () => { const $newSibling = getRandomElementInBody(); const $iframe = document.querySelector('iframe'); document.body.removeChild($iframe); document.body.insertBefore($iframe, $newSibling); }); if (!("moveBefore" in Element.prototype)) { document.querySelector('.warning').style.display = 'block'; }
如果您的浏览器不支持moveBefore
,请观看此视频以了解其实际效果。YouTube 嵌入(即 iframe)会随着 iframe 的移动而持续播放。
MutationObserver
对Web 组件的影响
如果您有MutationObserver
,则使用moveBefore
将(就像insertBefore
)生成两个突变:一个用于删除,一个用于将元素添加到其新父级。 出于兼容性原因,做出了这样的选择。
使用 Web 组件时,connectedMoveCallback
如果您指定了该方法,则会触发该方法。如果您未指定方法 connectedMoveCallback
,则常规的disconnectedCallback
和connectedCallback
将触发(同样是出于向后兼容的原因),并且isConnected
为true
。
浏览器支持
截至撰写本文时,浏览器支持仅限于 Chrome 133+。Safari 和 Firefox 均已表示支持此新 API。
moveBefore
您可以按如下方式检测功能的可用性:
if (!("moveBefore" in Element.prototype)) { // Not supported }
网友评论文明上网理性发言 已有0人参与
发表评论: