Chrome中为何出现未样式化文本闪烁?已做FOUC优化仍存问题
字体闪烁问题:Chrome下的成因与原生JS解决方案
问题背景
我有一个包含各类脚本、样式表及Google Fonts的HTML文档,已针对防止未样式化内容显示做了大量优化。相关代码片段如下:
<head> <title>my site</title> <link rel="stylesheet" media="screen" href="/static/index.css"> <script defer src="/static/shared.js"></script> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Wire+One&display=swap" rel="stylesheet"> <style type="text/css">.fouc-hidden {display: none}</style> <script type="text/javascript"> document.querySelector("html").classList.add("fouc-hidden") window.onload = () => { document.fonts.ready.then(() => { setTimeout(() => { console.log('done waiting') document.querySelector("html").classList.remove("fouc-hidden") }, 5000) }) } </script> </head> <body> <!-- 页面文本内容 --> </body>
操作步骤:
- 请求所有字体样式、其他样式表及脚本
- 定义
.fouc-hidden类的样式 - 为
html元素添加该类 - 等待
window.onload、document.fonts.ready完成后再延迟5秒,移除.fouc-hidden类时仍出现错误字体的文本闪烁
注:此时其他样式(如颜色)已正常应用,仅字体未样式化导致闪烁。
Chrome中的成因分析
document.fonts.ready触发时机偏差:该Promise触发仅代表字体文件已下载并注册,但Chrome渲染引擎可能还未完成字体与DOM元素的绑定、布局计算。此时显示内容,浏览器会先渲染 fallback 字体,再快速切换到目标字体,造成闪烁。window.onload冗余等待:window.onload会等待所有资源(图片、脚本等)加载完成,而字体加载完成时机早于该事件,额外等待无法解决字体渲染延迟问题,反而拉长用户等待时间。font-display: swap的影响:display=swap会让浏览器在字体加载完成前先显示 fallback 字体,即使隐藏了内容,移除隐藏类时,浏览器仍会先渲染 fallback 字体再替换目标字体,这是闪烁的直接原因。
原生JS解决方案
方案1:结合document.fonts.onloadingdone与requestAnimationFrame
放弃window.onload等待,直接监听字体加载完成事件,通过requestAnimationFrame确保渲染周期完成后再显示内容:
<head> <title>my site</title> <link rel="stylesheet" media="screen" href="/static/index.css"> <script defer src="/static/shared.js"></script> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <!-- 替换font-display为block,避免fallback字体提前渲染 --> <link href="https://fonts.googleapis.com/css2?family=Wire+One&display=block" rel="stylesheet"> <style type="text/css"> .fouc-hidden { visibility: hidden; /* 用visibility替代display:none,保留布局,避免显示时重排闪烁 */ } </style> <script type="text/javascript"> document.documentElement.classList.add("fouc-hidden"); document.fonts.onloadingdone = (event) => { // 确认所有字体都已加载完成 const allFontsLoaded = event.fontfaces.every(font => font.status === 'loaded'); if (allFontsLoaded) { // 等待下一次渲染周期,确保字体已绑定到DOM requestAnimationFrame(() => { document.documentElement.classList.remove("fouc-hidden"); }); } }; // 兜底:字体加载超时则直接显示内容 setTimeout(() => { document.documentElement.classList.remove("fouc-hidden"); }, 8000); </script> </head>
方案2:font-display: block配合CSS字体加载策略
将Google Fonts的display=swap改为display:block,让浏览器在字体加载完成前隐藏文本(最多隐藏3秒,超时则显示fallback字体),再配合JS的字体就绪监听实现双重保障:
font-display:block避免fallback字体提前渲染,减少闪烁可能- JS监听字体就绪后显示内容,确保字体加载完成后再渲染
关键优化点
- 用
visibility: hidden替代display: none:保留页面布局,避免显示时的重排重绘闪烁 - 移除
window.onload等待:字体加载完成早于其他资源,无需等待所有资源加载 - 替换
font-display属性:从swap改为block,消除fallback字体的提前渲染 - 添加超时兜底:防止字体加载失败时页面一直隐藏
内容的提问来源于stack exchange,提问作者Aaron Parisi
相关产品推荐
相关产品推荐

