How to Use the Fullscreen API in JavaScript (and Keep the Screen Awake with the Wake Lock API)

TL;DR · AI 摘要
How to Use the Fullscreen API in JavaScript and Keep the Screen Awake with the Wake Lock API September 29, 2026 / JavaSc...
核心要点
- 主题聚焦:How to Use the Fullscreen API in JavaScript and
- 来源:freeCodeCamp.org,建议结合原文判断细节。
- AI 分析暂不可用,本条为保底评分与摘要。
如何在 JavaScript 中使用全屏 API(并使用 Wake Lock API 保持屏幕常亮)
2026 年 9 月 29 日
/
#JavaScript
Alex Oliinyk
迟早,大多数前端开发者都会遇到同样的问题:"这个能占满整个屏幕吗?"
幻灯片、视频播放器、信息亭仪表板、游戏、绘图画布或教室投影仪上的计时器,只要浏览器标签栏和地址栏还在屏幕边缘显示,就会显得不完整。
好消息是浏览器内置了答案:全屏 API。坏消息是它伴随着一些容易被忽视的特性:用户手势、Safari 前缀、iPhone 的拒绝行为,以及在全屏体验进行两分钟后屏幕会自动熄灭。
在本教程中,你将学习如何:
- 将任意元素(或整个页面)进入全屏模式并退出
- 当用户按下 Esc 键时保持 UI 同步
- 使用 :fullscreen 伪类样式化全屏内容
- 处理跨浏览器陷阱,包括仍然不支持该功能的平台
- 使用屏幕唤醒锁 API 防止屏幕变暗
- 将所有内容整合成一个小型、可复用的模式
这里的所有内容都是原生 JavaScript,不使用任何库或构建步骤。你应该熟悉 DOM 事件和 async/await。如果你需要复习后者,freeCodeCamp 有一篇关于 async/await 的教程,涵盖了我们所有依赖的内容。
目录
- 全屏 API 的工作原理
- 进入和退出全屏
- 用户手势规则
- 通过 fullscreenchange 保持 UI 同步
- 样式化全屏内容
- 跨浏览器陷阱
- 使用 Wake Lock API 保持屏幕常亮
- 整合所有功能
- 快速陷阱检查清单
- 结论
全屏 API 的工作原理
全屏 API 很简洁。它提供了你每天都会用到的三个功能:
- element.requestFullscreen():请求浏览器使用整个屏幕显示该元素(及其子元素)。它返回一个 Promise。
- document.exitFullscreen():退出全屏模式。同样返回一个 Promise。
- document.fullscreenElement:当前处于全屏模式的元素,如果没有则为 null。这是判断"当前是否处于全屏模式"的唯一数据来源。
文档上还有两个事件:fullscreenchange(在进入或退出全屏时触发)和 fullscreenerror(请求失败时触发)。
一个令人惊讶的事实是:你可以让任意元素进入全屏模式,而不仅仅是整个页面。如果你对一个 <div> 调用全屏模式,只有该 <div> 会填满屏幕,文档中的其他内容会隐藏在它后面。
要让整个页面进入全屏模式,你需要在 document.documentElement(即 <html> 元素)上调用该方法。
MDN 的全屏 API 页面上有完整的参考文档,但你只需要了解上面提到的内容即可。
进入和退出全屏
让我们从最简单的实用功能开始:一个可以切换页面全屏状态的按钮。
<button id="fs-toggle">Fullscreen</button>const toggleBtn = document.getElementById('fs-toggle');
async function toggleFullscreen() {
if (!document.fullscreenElement) {
// 还没有元素处于全屏状态 – 进入全屏
await document.documentElement.requestFullscreen();
} else {
// 有元素处于全屏状态 – 退出全屏
await document.exitFullscreen();
}
}
toggleBtn.addEventListener('click', toggleFullscreen);这便是理想情况下的完整流程。由于两种方法都返回 Promise,因此你可以使用 await 等待它们完成,确保在执行后续代码前全屏状态已切换完毕。
但这两个调用都可能失败。例如,当文档不允许进入全屏状态(稍后会解释原因),或当你在没有全屏元素时调用 exitFullscreen()。因此在实际代码中,应使用 try/catch 包裹这些调用,而不是让未处理的拒绝状态出现在控制台中:
async function toggleFullscreen() {
try {
if (!document.fullscreenElement) {
await document.documentElement.requestFullscreen();
} else {
await document.exitFullscreen();
}
} catch (err) {
console.warn(`Fullscreen failed: ${err.name} – ${err.message}`);
}
}用户手势规则
这里出现了第一个陷阱,这也是 Stack Overflow 上最常见的疑问来源:你无法自动进入全屏状态。无论是页面加载时、定时器触发后,还是对网络事件的响应,都不行。
requestFullscreen() 仅在页面具有所谓"短暂激活"(transient activation)时才有效:这是用户真正与页面交互(如点击、轻点或按键)后短暂的窗口期。如果在该窗口期外调用,Promise 会直接拒绝,大多数浏览器会显示类似"API 必须由用户手势触发"的提示。
这是有意为之的设计。如果没有这个限制,任何页面都可以在加载时立即劫持你的整个屏幕,这正是钓鱼页面最想做的事情。
对代码来说,这意味着两个实践准则:
- 始终从与用户输入绑定的事件处理程序中触发全屏:如 click、keydown、pointerup 等事件。
- 不要在 requestFullscreen() 调用前 await 任何耗时操作。如果先 await fetch(...),激活窗口可能在你请求全屏时已经过期。
应优先调用 requestFullscreen(),再执行耗时操作。否则激活窗口可能在你请求前就关闭了。
一个常见且自然的模式是让键盘快捷键执行与按钮相同的操作:
document.addEventListener('keydown', (e) => {
// 忽略用户在输入框内输入时的快捷键
if (e.target.matches('input, textarea, [contenteditable]')) return;
if (e.key === 'f' || e.key === 'F') {
e.preventDefault();
toggleFullscreen();
}
});首次实现时我犯过两个小错误,值得分享:
第一,确保全屏区域本身可通过键盘访问:为其添加 tabindex="0",并将 Enter 和 Space 键的处理方式等同于点击,否则键盘用户会遇到一个没人告诉他们的按钮。
第二,考虑你的快捷键是否与页面实际功能冲突。我有一个"黑客打字"页面,任何按键都会生成假终端输出,包括 F 键。前几天里,用户在激烈打字时每次输入 F 都会中断全屏状态。最终的解决方案很简单:在这个页面上,F 键只用于进入全屏,退出全屏则专属于 Esc 键。
这引出了 Esc 键的处理:你不能自己处理它。所有浏览器在按下 Esc 时都会退出全屏,且无法阻止这一行为。这是安全的逃生机制。你可以做的是响应这个事件,这将在下一节讨论。
保持 UI 与 fullscreenchange 事件同步
由于用户可以通过你的代码无法控制的方式退出全屏(例如按下 Esc 键、使用浏览器自身的退出按钮,或在移动设备上切换应用),因此绝不应该用你自己的变量来跟踪全屏状态。这会导致状态不同步。
应始终将 document.fullscreenElement 视为唯一可信来源,并在 fullscreenchange 事件触发时同步更新 UI:
function syncFullscreenUI() {
const isFullscreen = Boolean(document.fullscreenElement);
toggleBtn.textContent = isFullscreen ? 'Exit fullscreen' : 'Fullscreen';
toggleBtn.setAttribute('aria-pressed', String(isFullscreen));
document.body.classList.toggle('is-fullscreen', isFullscreen);
}
document.addEventListener('fullscreenchange', syncFullscreenUI);
document.addEventListener('fullscreenerror', () => {
console.warn('Could not enter fullscreen.');
});这个监听器能覆盖所有进入和退出全屏的路径(包括你未主动触发的场景)。这也是暂停动画、恢复游戏循环或(如你稍后将看到的)释放唤醒锁的合适位置。
全屏内容的样式设计
CSS 为全屏状态提供了两个样式钩子。
:fullscreen 伪类匹配当前处于全屏状态的元素。浏览器会应用默认样式表将元素拉伸至全屏视口,但你通常需要自己控制背景和溢出等属性:
:fullscreen {
background: #000;
overflow: hidden;
cursor: none; /* 在闲置的全屏界面上隐藏指针 */
}当页面是纯黑色时,cursor: none 的效果在 OLED 显示屏上尤为明显:每个像素都熄灭,鼠标指针是面板上唯一亮着的部分。在进入全屏后隐藏指针,能区分出「屏幕完全熄灭」和「屏幕中间有个小白箭头」两种状态。
::backdrop 伪元素是绘制在全屏元素背后的图层。它只在全屏元素未覆盖整个屏幕时生效(例如固定宽高比的元素),并用于控制黑边区域的颜色:
:fullscreen::backdrop {
background: #000;
}实用技巧:如果你要对整个页面进行全屏显示,请在 <html> 而非 <body> 上设置背景色。在全屏状态下,显示的是 <html> 元素,某些浏览器在仅设置 <body> 背景色时,过渡过程中边缘会出现错误颜色的细条。
跨浏览器陷阱
全屏 API 在 Chrome、Edge 和 Firefox 中已标准化多年,但 Safari 才是需要重点关注的浏览器。
旧版 Safari 需要 webkit 前缀
Safari 直到 16.4 版本(2023 年春季)才在 macOS 和 iPadOS 上提供无前缀的 API。此前版本使用 webkitRequestFullscreen()、webkitExitFullscreen()、webkitFullscreenElement 和 webkitfullscreenchange 事件。除非你能忽略三年前的 Safari 安装版本,否则添加一个极小的兼容性层是值得的:
const fs = {
get element() {
return document.fullscreenElement ?? document.webkitFullscreenElement ?? null;
},
get enabled() {
return Boolean(document.fullscreenEnabled ?? document.webkitFullscreenEnabled);
},
request(el = document.documentElement) {
if (el.requestFullscreen) return el.requestFullscreen();
if (el.webkitRequestFullscreen) return Promise.resolve(el.webkitRequestFullscreen());
return Promise.reject(new Error('Fullscreen not supported'));
},
exit() {
if (document.exitFullscreen) return document.exitFullscreen();
if (document.webkitExitFullscreen) return Promise.resolve(document.webkitExitFullscreen());
return Promise.reject(new Error('Fullscreen not supported'));
},
};
// 监听两个事件名称以确保旧版 Safari 同步工作。
['fullscreenchange', 'webkitfullscreenchange'].forEach((evt) =>
document.addEventListener(evt, syncFullscreenUI)
);现在 fs.request() 和 fs.exit() 在所有浏览器中都返回 Promise,其余代码无需关心具体实现。你会看到推荐使用 screenfull.js 的建议。这是一个不错的库,但已被声明为功能完整且冻结,上述十五行代码就是它原本提供的全部功能。
iPhone Safari 完全不支持元素全屏
这是让所有人措手不及的陷阱。截至撰写本文时,iPhone 上的 Safari 仅支持 <video> 元素的全屏 API,不支持任意元素。它在 iPad 和 Mac 上可以正常工作,但在手机上 document.fullscreenEnabled 为 false,<div> 元素上也没有 requestFullscreen 方法。由于 iOS 上的 Chrome 和 Firefox 必须使用 Safari 的引擎,它们继承了同样的限制。
你有两个可行的方案:
- 伪全屏回退方案:使用 position: fixed; inset: 0 定位元素并隐藏自己的浏览器界面。你无法移除 Safari 的地址栏,但它在滚动时会折叠,对于大多数工具来说这已经足够。
- 建议“添加到主屏幕”:通过 manifest 中设置 "display": "standalone" 的 Web 应用从主屏幕启动时,将不显示任何浏览器界面。这是目前在 iPhone 上实现真正无边框内容的唯一方式。
无论选择哪种方案,都不要仅依赖特性检测。document.fullscreenEnabled 只能告诉你 API 是否存在,但运行时仍可能被拒绝(例如在没有 allowfullscreen 属性的 iframe 中、在被浏览器策略锁定的页面上,或因为某个你未测试的浏览器有自己不同的判断标准)。
对我而言行之有效的模式是尝试真实 API,将拒绝视为触发 CSS 回退的信号:
function enterFullscreen(el) {
if (fs.enabled) {
return fs.request(el).catch(() => {
// API 存在但被拒绝 – 降级为固定覆盖层。
document.body.classList.add('pseudo-fullscreen');
});
}
document.body.classList.add('pseudo-fullscreen');
return Promise.resolve();
}在手机上,我更进一步,即使在理论上支持的 Android 上也完全不调用 API。由于地址栏在滚动时会重新出现,系统手势滑动等交互,固定覆盖层在小触摸屏上的行为比真实全屏更可预测。这实际上也是 iPhone Safari 强制你采用的路径。减少一个需要测试的分支。
在决定需要多少兼容性处理之前,你可以先查看 Can I use 上的当前支持情况表。
使用 Wake Lock API 保持屏幕常亮
你已经构建了一个令人惊叹的全屏体验。用户靠后观看它……九十秒后屏幕变暗并锁定,因为操作系统没有检测到任何输入,假设没有人存在。
对于视频元素,浏览器会为你处理这种情况。对于其他任何内容(如倒计时、幻灯片、画布动画、食谱或乐谱页面),你需要使用 Screen Wake Lock API。该 API 在 2024 年已登陆所有主流浏览器(Chrome 84+、Safari 16.4+、Firefox 126+),并在 2025 年成为 Baseline 的一部分,因此你现在可以直接使用,无需 polyfill。
API 仅需一次调用,且基于 Promise:
let wakeLock = null;
async function requestWakeLock() {
if (!('wakeLock' in navigator)) return; // 不支持 – 静默失败
try {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
// 操作系统或浏览器释放了锁(标签隐藏、省电模式等)
wakeLock = null;
});
} catch (err) {
// 被拒绝 – 通常是因为设备处于省电模式
console.warn(`锁屏失败: ${err.name} – ${err.message}`);
}
}
async function releaseWakeLock() {
if (wakeLock) {
await wakeLock.release();
wakeLock = null;
}
}需要了解的三条规则:
- 需要安全上下文。该 API 仅在 HTTPS(以及 localhost)环境下可用。
- 当页面隐藏时,浏览器会自动释放锁。如果用户切换标签页、最小化窗口或锁定手机,当页面再次可见时,锁不会自动恢复。你必须重新请求:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && shouldStayAwake()) {
requestWakeLock();
}
});- 做个好公民。锁屏会消耗电池。只有在真正有用的时候(如播放、运行或显示内容时)才保持锁屏。一旦不再需要,立即释放。
好消息是 Fullscreen API 已经为你提供了完美的信号:“正在显示内容”:fullscreenchange。当用户进入全屏时请求锁,当他们退出全屏时释放锁。
不过全屏并不是唯一的良好信号,将锁屏视为与活动相关而非显示模式相关会更有帮助。在倒计时器上,我会在倒计时开始时请求锁,在倒计时到达零或暂停时释放锁(无论页面是否处于全屏状态),因为没有人希望定时器在 90 秒时变暗。在雨声播放器上,锁屏会跟随音频:播放时获取锁,暂停或睡眠计时器淡出声音时释放锁。
机制完全相同:唯一变化的是“现在屏幕是否应该保持唤醒?”这个问题。
综合应用
以下是完整模式的集中展示:一个带有键盘快捷键的全屏切换按钮、一个跟随全屏状态的锁屏、无论用户如何离开都能保持 UI 同步,以及一个针对不支持浏览器的回退方案。
const toggleBtn = document.getElementById('fs-toggle');
let wakeLock = null;/* ---------- 全屏兼容层 ---------- */ const fs = { get element() { return document.fullscreenElement ?? document.webkitFullscreenElement ?? null; }, get enabled() { return Boolean(document.fullscreenEnabled ?? document.webkitFullscreenEnabled); }, request(el = document.documentElement) { if (el.requestFullscreen) return el.requestFullscreen(); if (el.webkitRequestFullscreen) return Promise.resolve(el.webkitRequestFullscreen()); return Promise.reject(new Error('Fullscreen not supported')); }, exit() { if (document.exitFullscreen) return document.exitFullscreen(); if (document.webkitExitFullscreen) return Promise.resolve(document.webkitExitFullscreen()); return Promise.reject(new Error('Fullscreen not supported')); }, };
/* ---------- 屏幕唤醒锁 ---------- */ async function requestWakeLock() { if (!('wakeLock' in navigator) || wakeLock) return; try { wakeLock = await navigator.wakeLock.request('screen'); wakeLock.addEventListener('release', () => { wakeLock = null; }); } catch (err) { console.warn(唤醒锁获取失败: ${err.name}); } }
async function releaseWakeLock() { if (!wakeLock) return; await wakeLock.release(); wakeLock = null; }
/* ---------- 切换功能 ---------- */ async function toggleFullscreen() { const pseudo = document.body.classList.contains('pseudo-fullscreen'); try { if (pseudo) { document.body.classList.remove('pseudo-fullscreen'); onFullscreenChange(); } else if (!fs.element) { await fs.request(); } else { await fs.exit(); } } catch (err) { // API被拒绝(iframe策略、不支持的平台等)– 优雅降级 document.body.classList.add('pseudo-fullscreen'); onFullscreenChange(); } }
/* ---------- 保持状态同步 ---------- */ function onFullscreenChange() { const active = Boolean(fs.element) || document.body.classList.contains('pseudo-fullscreen'); toggleBtn.textContent = active ? '退出全屏' : '全屏'; toggleBtn.setAttribute('aria-pressed', String(active)); document.body.classList.toggle('is-fullscreen', active);
// 屏幕唤醒时长应与全屏状态严格同步 if (active) requestWakeLock(); else releaseWakeLock(); }
['fullscreenchange', 'webkitfullscreenchange'].forEach((evt) => document.addEventListener(evt, onFullscreenChange) );
// 如果标签页被隐藏后又恢复且处于全屏状态,重新获取唤醒锁 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible' && fs.element) requestWakeLock(); });
/* ---------- 功能连接 ---------- */ toggleBtn.addEventListener('click', toggleFullscreen); document.addEventListener('keydown', (e) => { if (e.target.matches('input, textarea, [contenteditable]')) return; if (e.key === 'f' || e.key === 'F') { e.preventDefault(); toggleFullscreen(); } }); // 不支持的平台(如iPhone Safari)会在第一次点击时直接进入伪全屏状态 // 不需要用户代理检测 // 另一个移动端特性:设备旋转后,视口变化会在事件触发后发生 // 因此需要延迟重新测量画布或布局 const resizeStage = () => { /* 重新测量填充屏幕的元素 */ }; window.addEventListener('orientationchange', () => setTimeout(resizeStage, 100));
对应的CSS样式:
html { background: #000; }
:fullscreen { overflow: hidden; cursor: none; } :fullscreen::backdrop { background: #000; }
/* 无元素全屏功能的浏览器回退方案 */ body.pseudo-fullscreen .app { position: fixed; inset: 0; } body.pseudo-fullscreen .chrome { display: none; }
大约六十行代码,这正是我在生产环境中运行的核心代码:我会将每次全屏进入操作记录为分析事件,因为在屏幕工具类网站上,这种状态转换就是转化行为,而正是这段代码触发了该事件。
例如,它驱动了 blankscreen.io 上的黑色屏幕页面——这个页面的唯一功能就是让显示器完全变黑,通过点击或按下 F 键进入全屏模式,隐藏指针,在显示期间保持屏幕常亮,并在按下 Esc 键时干净地退出。如果你在 iPhone 上打开这个页面,会看到上面提到的伪全屏回退方案在发挥作用。
在实际应用中,这种模式的使用方式类似:单击或按下 F 键,整个显示器变黑,屏幕会持续保持常亮直到关闭。按下 Esc 键即可恢复浏览器界面。
一旦掌握这种模式,将其添加到计时器、幻灯片或画布实验中就变得简单,只需将想要填充屏幕的元素插入即可。
快速注意事项清单
在发布前请检查以下事项。每项内容都是我至少亲身经历过一次的坑:
- 需要用户手势:禁止在页面加载、定时器或长时间等待的 await 后直接触发全屏。
- 不要自行跟踪状态:使用 document.fullscreenElement 并监听 fullscreenchange 事件。
- 无法阻止 Esc 键:设计时要接受用户随时可能离开。
- 旧版 Safari 需要 webkit 前缀:方法名和事件名都需要添加。
- iPhone Safari 不支持元素级全屏:通过 document.fullscreenEnabled 进行特性检测,将运行时拒绝视为回退触发,而不仅是控制台警告。
- 检查快捷键与页面已有按键的冲突:如果应用本身消费字母键,应确保快捷键仅用于进入/退出全屏,Esc 键保留退出功能。
- 确保全屏区域可通过键盘访问(添加 tabindex="0",使用 Enter/Space 触发)。
- 唤醒锁需要 HTTPS:可能在省电模式下被拒绝,页面隐藏时会失效,需在 visibilitychange 时重新请求。
- 一旦触发全屏的原因消失,立即释放唤醒锁。
- 全屏时将背景设置在 html 元素上,而不仅仅是 body 元素。
结论
全屏 API 是那种初看像单行代码、实际却有鲜明个性的浏览器特性。
核心部分(requestFullscreen()、exitFullscreen()、fullscreenElement 和 fullscreenchange 事件)确实很简单。真正的难点在于遵守用户手势规则、保持 UI 状态的真实性、处理 Safari 的历史记录,以及记住:如果屏幕两分钟后变黑,"全屏"只完成了任务的一半。唤醒锁 API 恰好弥补了这个缺口,通过 fullscreenchange 事件将两者结合使用,可以保持两者都整洁有序。
将上面六十行的模式套用,插入你自己的元素,你就能为从演示文稿到游戏的任何场景打造一个生产级的全屏体验。
编码愉快!
推荐课程(来自合作伙伴)
(在新标签页中打开)
广告
- Udemy 上的课程:使用 Python 和 Playwright 进行 Web 自动化测试 + 视觉识别 Simon Storozhenko 4.5 星(1512 人评价) 7小时20分钟 初学者 证书 查看课程(在新标签页中打开)
- 在 Udemy 上《用户界面设计基础》Joe Natoli 4.5(共5颗星)(18,605人评价)9小时56分钟 初学者 证书 查看课程(在新标签页中打开)
- 在 Coursera 上《初学者的JavaScript入门》Anton Voroniuk 8周,每周1小时 初学者 证书 查看课程(在新标签页中打开)
- 在 Coursera 上《Web开发者的AJAX》Mau Fournier 3周,每周1小时 中级 证书 查看课程(在新标签页中打开)
Class Central 上的更多JavaScript课程
→
独立网页开发者和出版人。自2016年以来一直从事浏览器游戏和参考网站的开发——包括Chrome Dino粉丝项目chromedino.com。我撰写关于浏览器游戏、隐藏功能和简约软件开发技巧的文章。
如果这篇文章对你有帮助,请分享它。
免费学习编程。freeCodeCamp的开源课程已帮助超过40,000人成为开发者。立即开始