freeCodeCamp.org

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

7.1内容质量
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 分析暂不可用,本条为保底评分与摘要。
#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 页面上有完整的参考文档,但你只需要了解上面提到的内容即可。

进入和退出全屏

让我们从最简单的实用功能开始:一个可以切换页面全屏状态的按钮。

code
<button id="fs-toggle">Fullscreen</button>
javascript
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 包裹这些调用,而不是让未处理的拒绝状态出现在控制台中:

code
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(),再执行耗时操作。否则激活窗口可能在你请求前就关闭了。

一个常见且自然的模式是让键盘快捷键执行与按钮相同的操作:

code
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:

code
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 伪类匹配当前处于全屏状态的元素。浏览器会应用默认样式表将元素拉伸至全屏视口,但你通常需要自己控制背景和溢出等属性:

code
:fullscreen {
  background: #000;
  overflow: hidden;
  cursor: none; /* 在闲置的全屏界面上隐藏指针 */
}

当页面是纯黑色时,cursor: none 的效果在 OLED 显示屏上尤为明显:每个像素都熄灭,鼠标指针是面板上唯一亮着的部分。在进入全屏后隐藏指针,能区分出「屏幕完全熄灭」和「屏幕中间有个小白箭头」两种状态。

::backdrop 伪元素是绘制在全屏元素背后的图层。它只在全屏元素未覆盖整个屏幕时生效(例如固定宽高比的元素),并用于控制黑边区域的颜色:

code
: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 安装版本,否则添加一个极小的兼容性层是值得的:

code
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 回退的信号:

code
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:

code
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)环境下可用。
  • 当页面隐藏时,浏览器会自动释放锁。如果用户切换标签页、最小化窗口或锁定手机,当页面再次可见时,锁不会自动恢复。你必须重新请求:
code
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible' && shouldStayAwake()) {
    requestWakeLock();
  }
});
  • 做个好公民。锁屏会消耗电池。只有在真正有用的时候(如播放、运行或显示内容时)才保持锁屏。一旦不再需要,立即释放。

好消息是 Fullscreen API 已经为你提供了完美的信号:“正在显示内容”:fullscreenchange。当用户进入全屏时请求锁,当他们退出全屏时释放锁。

不过全屏并不是唯一的良好信号,将锁屏视为与活动相关而非显示模式相关会更有帮助。在倒计时器上,我会在倒计时开始时请求锁,在倒计时到达零或暂停时释放锁(无论页面是否处于全屏状态),因为没有人希望定时器在 90 秒时变暗。在雨声播放器上,锁屏会跟随音频:播放时获取锁,暂停或睡眠计时器淡出声音时释放锁。

机制完全相同:唯一变化的是“现在屏幕是否应该保持唤醒?”这个问题。

综合应用

以下是完整模式的集中展示:一个带有键盘快捷键的全屏切换按钮、一个跟随全屏状态的锁屏、无论用户如何离开都能保持 UI 同步,以及一个针对不支持浏览器的回退方案。

code
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));

code

对应的CSS样式:

html { background: #000; }

code

:fullscreen { overflow: hidden; cursor: none; } :fullscreen::backdrop { background: #000; }

/* 无元素全屏功能的浏览器回退方案 */ body.pseudo-fullscreen .app { position: fixed; inset: 0; } body.pseudo-fullscreen .chrome { display: none; }

code

大约六十行代码,这正是我在生产环境中运行的核心代码:我会将每次全屏进入操作记录为分析事件,因为在屏幕工具类网站上,这种状态转换就是转化行为,而正是这段代码触发了该事件。

例如,它驱动了 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分钟 初学者 证书 查看课程(在新标签页中打开)
code

- 在 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人成为开发者。立即开始