
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人参与
发表评论: