能力 · API
Window Management API
发布于
Window Management API 让页面得知设备有几块屏幕、各在什么位置,然后把窗口放到指定屏幕上。window.screen.isExtended 无需提示即可回答第一个问题;window.getScreenDetails() 请求 window-management 权限,并以 ScreenDetails 对象兑现,其 screens 数组为每块显示器提供一个 ScreenDetailed,坐标相对于多屏原点,这样 window.open() 的 features 与 requestFullscreen({ screen }) 就能指向某块显示器。
Chrome 100 在桌面端发布了 getScreenDetails()、Screen.isExtended 和 ScreenDetailed;Edge 与 Opera 继承该实现,Android 版 Chrome 暴露相同接口。Firefox 和 Safari 任何版本都没有实现(BCD api.Window.getScreenDetails)。所有接口都带 [SecureContext]。
window.screen.isExtended
window.getScreenDetails()
screenDetails.screensscreenDetails.currentScreen
element.requestFullscreen({ screen: screenDetailed })isExtended 是现有 Screen 接口上的同步 boolean。getScreenDetails() 返回 Promise<ScreenDetails>;对同一个 Window 每次调用返回同一个 ScreenDetails 对象,显示器增减时触发 screenschange,窗口移到另一块显示器或所在屏幕的属性变化时触发 currentscreenchange。ScreenDetailed 继承自 Screen,所以除下表成员外还有 width、height、availWidth、availHeight、colorDepth 和 orientation。
getScreenDetails() 不接受参数。它返回的对象携带以下成员;FullscreenOptions 的 screen 成员是该 API 新增的唯一输入。
| 成员 | 类型 | 必填 | 说明 |
|---|---|---|---|
ScreenDetails.screens |
FrozenArray<ScreenDetailed> |
只读 | 全部屏幕,先按 left 再按 top 排序。集合变化时换成新数组。 |
ScreenDetails.currentScreen |
ScreenDetailed |
只读 | 窗口所在的屏幕;与 screens 中的某一项 ===,所以 screens.find(s => s !== currentScreen) 可以选出副屏。 |
ScreenDetailed.left、ScreenDetailed.top |
long |
只读 | 多屏原点到该屏幕左边缘、上边缘的距离,单位 CSS 像素。 |
ScreenDetailed.availLeft、ScreenDetailed.availTop |
long |
只读 | 同上,但针对排除任务栏与 Dock 的可用区域。在 window.open() 的 features 里用作 left/top。 |
ScreenDetailed.isPrimary |
boolean |
只读 | 操作系统指定的主显示器为 true。 |
ScreenDetailed.isInternal |
boolean |
只读 | 笔记本屏幕之类的内置面板为 true,外接显示器为 false。 |
ScreenDetailed.devicePixelRatio |
float |
只读 | 该屏幕的物理像素与 CSS 像素之比,可能不同于 window.devicePixelRatio。 |
ScreenDetailed.label |
DOMString |
只读 | 操作系统提供的名称,例如 "Built-in Retina Display" 或 "DELL U2720Q";可能为空。 |
FullscreenOptions.screen |
ScreenDetailed |
否 | 要求 requestFullscreen() 先把窗口移到该屏幕。浏览器可以转而尊重用户偏好。 |
规范为 getScreenDetails() 定义了一种拒绝名称,并在相关的 minimize()、maximize()、restore() 和 setResizable() 方法中复用(见 getScreenDetails() 方法(w3.org))。
| 异常 | 条件 |
|---|---|
NotAllowedError |
文档不被允许使用 window-management Permissions Policy 特性(默认允许列表为 'self');或权限请求的结果为 "denied",原因是用户拒绝了提示,或者调用时没有瞬时激活而权限仍是 "prompt"。窗口显示状态方法在文档不是已安装 Web 应用、缺少瞬时激活或操作系统拒绝更改时也以它拒绝。 |
isExtended 不抛任何异常,Permissions Policy 拦截该特性时返回 false,所以 false 的含义是「单屏,或不被允许知道」。screen 选项若指向另一个窗口的 ScreenDetails 里的 ScreenDetailed,会被忽略而不是拒绝。
每个示例都退化为单屏行为:Firefox 和 Safari 永远走不到多屏分支,Chrome 上用户拒绝权限时该分支也会被跳过。
在副屏上打开伴随窗口
Section titled “在副屏上打开伴随窗口”先检查 isExtended(无提示),在 click 处理函数内调用 getScreenDetails() 以便允许弹出权限提示,任何失败都回退到普通的 window.open()。
button.addEventListener("click", async () => { const url = "/presenter-notes"; if (!("getScreenDetails" in window) || !window.screen.isExtended) { window.open(url, "_blank", "popup"); return; } try { const details = await window.getScreenDetails(); const target = details.screens.find((s) => s !== details.currentScreen) ?? details.currentScreen; const features = `popup,left=${target.availLeft},top=${target.availTop},width=${target.availWidth},height=${target.availHeight}`; window.open(url, "_blank", features); } catch (err) { console.warn(`${err.name}: ${err.message}`); window.open(url, "_blank", "popup"); }});权限授予后,features 字符串里的 left 和 top 按多屏原点解释,所以位于主屏左侧或上方的显示器用负坐标是合法的。
在外接显示器上全屏演示,笔记留在笔记本屏幕
Section titled “在外接显示器上全屏演示,笔记留在笔记本屏幕”把选中的 ScreenDetailed 作为 FullscreenOptions.screen 传入。规范允许一次成功的跨屏全屏请求为紧随其后的一次 window.open() 免去激活要求,这正是同一次点击还能打开笔记窗口的原因。
async function startPresentation(slides) { if (!("getScreenDetails" in window)) { return slides.requestFullscreen(); // 仅当前屏幕 } const details = await window.getScreenDetails(); const external = details.screens.find((s) => !s.isInternal); if (!external) return slides.requestFullscreen();
await slides.requestFullscreen({ screen: external }); const notes = details.screens.find((s) => s.isInternal) ?? details.currentScreen; window.open("/notes", "_blank", `popup,left=${notes.availLeft},top=${notes.availTop},width=800,height=600`);}每块屏幕都报告 isInternal: false(接两台显示器的台式机)时,代码回退到当前屏幕而不是猜测。
响应显示器的插入与移除
Section titled “响应显示器的插入与移除”screenschange 在 ScreenDetails 对象上触发,而不是在 window 上,并且 screens 数组是整体替换而非原地修改。在处理函数里重新读取它,并关闭所在屏幕已消失的伴随窗口。
async function watchScreens(onChange) { if (!("getScreenDetails" in window)) { window.screen.addEventListener("change", () => onChange([window.screen])); return; } const details = await window.getScreenDetails(); onChange(details.screens); details.addEventListener("screenschange", () => onChange(details.screens)); details.addEventListener("currentscreenchange", () => { document.documentElement.style.setProperty("--dpr", details.currentScreen.devicePixelRatio); });}单屏回退监听的是同一份规范为 Screen 新增的 change 事件,窗口当前屏幕的基本属性(尺寸、方向、像素比)变化时触发。
- display_override,控制这些屏幕上窗口边框样式的 manifest 成员
- Tabbed application mode 与 tab_strip
- 桌面端的 PWA
- Window Management: getScreenDetails() method(w3.org)
- Window Management: permission API integration(w3.org)
- Manage several displays with the Window Management API(developer.chrome.com)
- Chromium: window_screen_details.cc(chromium.googlesource.com)
规范
| 规范 | 状态 |
|---|---|
| Window Management(窗口管理) | W3C 草案 |
| Window Management: getScreenDetails() method | W3C 草案 |
| Window Management: ScreenDetailed interface | W3C 草案 |
| Window Management: extensions to FullscreenOptions | W3C 草案 |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 100 | 高 | 来源 | — |
| Chrome (Android) | 支持 | 100 | 高 | 来源 | 1 |
| Edge (Desktop) | 支持 | 100 | 高 | 来源 | 2 |
| Firefox (Desktop) | 不支持 | — | 高 | 来源 | 3 |
| Firefox (Android) | 不支持 | — | 高 | 来源 | 45 |
| Safari (macOS) | 不支持 | — | 高 | 来源 | 6 |
| Safari (iOS) | 不支持 | — | 高 | 来源 | 78 |
| Samsung Internet | 支持 | 19.0 | 高 | 来源 | 9 |
| WebView (Android) | 支持 | 100 | 高 | 来源 | 10 |
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- browser-compat-data 未记录 Firefox 的支持。
- browser-compat-data 未记录 Firefox for Android 的支持。
- 由 browser-compat-data 镜像自 Firefox 的数据推导。
- browser-compat-data 未记录 Safari 的支持。
- browser-compat-data 未记录 iOS 版 Safari 的支持。
- 由 browser-compat-data 镜像自 Safari 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。