浏览器能浮个视频出去,但浮个小组件出去这件事从来没做过——Firefox 151 今天把它原生化了
浏览器能浮个视频出去这件事,Chrome 2019 年就做了。但浮个小组件出去——比如把页面右上角的股票行情、待办事项、聊天面板拆出来,让它始终悬在桌面最上层——这件事从来没被原生支持过。
Firefox 151 说:我把它做出来了。
什么是 Document Picture-in-Picture
传统 Picture-in-Picture(PiP)只能锁定一个 <video> 元素。Document PiP 不一样——它可以在桌面层打开一个空白窗口,然后把任意 HTML 结构装进去。这个窗口是真正独立的浏览器窗口:可以拖动、可以调整大小、可以放在任何桌面上、切换标签页和切换应用都影响不了它。
实际应用场景很具体:股票行情实时浮窗、跨标签页的待办面板、视频会议的浮动控件、播放列表、实时协作编辑的小面板——这些以前要么靠 Electron 桌面应用,要么靠浏览器扩展,要么靠各种 hack 实现,现在一个 API 搞定。
浏览器支持现状
Chrome 116+ 和 Edge 116+ 早就有了。Firefox 151(2026 年 9 月)刚刚加入。Safari 还在排队,Can I Use 数据显示全球约 26% 覆盖,移动端完全不支持。需要 @supports 做渐进增强?别想了——这个 API 没有对应的 CSS feature query,at-rule() 函数只有 Chrome 支持,而且 WICG 已经把 display-mode: picture-in-picture 的支持计划撤回了。只能用 JavaScript 检测:
if (!(“documentPictureInPicture” in window)) {
// 不支持,移除按钮或做降级
document.querySelector(“.pip-button”).remove();
}
最小可运行例子
三步走:检测支持 → 打开窗口 → 把内容移进去。
第一步,检测:
const pipButton = document.querySelector(“.pip-button”);
pipButton.addEventListener(“click”, async () => {
if (!(“documentPictureInPicture” in window)) return;
});
第二步,打开窗口:
const pipWindow = await window.documentPictureInPicture.requestWindow({
width: 400,
height: 300
});
第三步,把目标元素移进去:
const widget = document.querySelector(“.my-widget”);
pipWindow.document.body.append(widget);
这样点一下按钮,你的 .my-widget 就从页面里消失了,出现在一个独立的浮动窗口里。
样式迁移:坑在这里
新开的 DPIP 窗口是空白的——没有任何样式。CSS 不会跟着元素一起过来,需要手动复制。
标准做法是用 createDocumentFragment 把所有样式表一次性移过去,避免多次 reflow:
const styles = Array.from(document.querySelectorAll(
‘link[rel=”stylesheet”]’, ‘style’
));
const fragment = document.createDocumentFragment();
styles.forEach(s => fragment.appendChild(s.cloneNode(true)));
pipWindow.document.head.appendChild(fragment);
然后给 DPIP 窗口里的内容写针对性的 CSS,用 @media (display-mode: picture-in-picture) 媒体查询:
@media (display-mode: picture-in-picture) {
.my-widget {
padding: 12px;
font-size: 14px;
}
}
还有一个坑:事件监听器
把元素移进 DPIP 窗口后,元素上的事件监听器不会自动跟随。需要重新绑定,或者用事件委托:
pipWindow.document.addEventListener(“click”, (e) => {
if (e.target.matches(“.my-widget .close-btn”)) {
pipWindow.close();
}
});
键盘焦点问题
打开 DPIP 窗口会抢走键盘焦点——你按 Ctrl+T 想开新标签,焦点可能被 DPIP 窗口截走。关掉 DPIP 或者先点一下标签栏还回来。WICG 已有 issue 追踪。
标题栏无法隐藏
出于安全考虑,DPIP 窗口的标题栏永远带着网站来源标识和关闭按钮——这是浏览器强制要求,防止钓鱼。可以用 disallowReturnToOpener 选项隐藏”返回原标签”按钮,但标题栏本身无法去掉。
能不能全屏
不能。requestFullscreen() 在 DPIP 窗口里被明确禁用,WICG Issue #133 已写入规范。
现在能不能用
能用,但要做降级:
async function openPip() {
if (!(“documentPictureInPicture” in window)) {
window.open(url, “widget”, “width=400,height=300”);
return;
}
const pipWindow = await documentPictureInPicture.requestWindow();
}
在 Chrome + Firefox 上这个 API 已经很稳。团队里如果有 Safari 用户,需要告诉他们这个功能暂时用不了,不要误以为是 bug。
下一步
如果你在做一个有可拆分小组件的产品——股票、待办、聊天、实时数据面板——现在可以用 Document PiP 做一个无依赖的浮动窗口版,不需要 Electron,不需要扩展,不需要 Service Worker。三步检测 + requestWindow + cloneNode,今天下午就能跑通 Demo。
评论区
登录后可评论。