MDN CSS动画示例中fade-out动画在Firefox与Safari中失效的问题求助
MDN CSS动画示例中fade-out动画在Firefox与Safari中失效的问题求助
大家好,我在复刻MDN「使用CSS动画」章节的最后一个切换显示/隐藏的示例时碰到了兼容性问题,想请各位帮忙看看:
问题现象
- 淡入(fade-in)动画完全正常:点击空白区域或按任意键,带红边框的div会平滑淡入显示
- 淡出(fade-out)动画失效:再次触发切换时,div会突然消失,完全看不到0.7秒的过渡效果
测试环境
- 失效的浏览器:
- Firefox 139.0.4 → 升级到140.0.0(MacOS Sonoma 14.7.4)后仍失效
- Safari 18.3.1 (19620.2.4.111.9, 19620)
- 正常的浏览器:Chrome 138.0.7204.50 (Official Build) (x86_64)
我当前的完整代码
HTML
<p> Click anywhere on the screen or press any key to toggle the <code><div></code> between hidden and showing. </p> <div> This is a <code><div></code> element that animates between <code>display: none; opacity: 0</code> and <code>display: block; opacity: 1</code>. Neat, huh? </div> <p> This is another paragraph to show that <code>display: none;</code> is being applied and removed on the above <code><div> </code>. If only its <code>opacity</code> was being changed, it would always take up the space in the DOM. </p>
CSS
html { height: 100vh; } div { font-size: 1.6rem; padding: 20px; border: 3px solid red; border-radius: 20px; width: 480px; opacity: 0; display: none; } /* Animation classes */ div.fade-in { display: block; animation: fade-in 0.7s ease-in forwards; } div.fade-out { animation: fade-out 0.7s ease-out forwards; } /* Animation keyframes */ @keyframes fade-in { 0% { opacity: 0; display: none; } 100% { opacity: 1; display: block; } } @keyframes fade-out { 0% { opacity: 1; display: block; } 100% { opacity: 0; display: none; } }
JavaScript
const divElem = document.querySelector("div"); const htmlElem = document.querySelector(":root"); htmlElem.addEventListener("click", showHide); document.addEventListener("keydown", showHide); function showHide() { if (divElem.classList[0] === "fade-in") { divElem.classList.remove("fade-in"); divElem.classList.add("fade-out"); } else { divElem.classList.remove("fade-out"); divElem.classList.add("fade-in"); } }
问题根源分析
这个问题的核心在于:display是CSS规范中明确的「不可动画属性」。
Chrome浏览器做了特殊兼容处理,会忽略动画中display的变化,优先执行opacity的过渡;但Firefox和Safari严格遵循规范,当动画关键帧中出现display: none时,浏览器会直接触发这个属性变更,导致整个淡出动画被“跳过”,元素直接消失。
解决方案
我们需要避开在动画中操作display,这里提供两种稳妥的实现方案:
方案1:用visibility替代display控制显示(推荐)
visibility是可动画属性,搭配opacity可以实现视觉隐藏+占位/不占位的效果(若要完全不占位,可配合height、padding等属性的过渡)。
修改后的关键CSS代码:
div { font-size: 1.6rem; padding: 20px; border: 3px solid red; border-radius: 20px; width: 480px; opacity: 0; visibility: hidden; /* 可选:完全消除占位,添加以下过渡属性 */ height: 0; padding: 0; border-width: 0; transition: height 0.7s ease-out, padding 0.7s ease-out, border-width 0.7s ease-out; } div.fade-in { visibility: visible; /* 恢复初始占位属性 */ height: auto; padding: 20px; border-width: 3px; animation: fade-in 0.7s ease-in forwards; } /* 动画关键帧仅控制opacity */ @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
方案2:用JS监听动画结束事件再设置display: none
保留display属性,但等fade-out动画完全结束后,再通过JS设置display: none,避免打断动画流程:
修改后的JavaScript代码:
function showHide() { if (divElem.classList.contains("fade-in")) { divElem.classList.remove("fade-in"); divElem.classList.add("fade-out"); // 单次监听动画结束事件 divElem.addEventListener("animationend", function handleFadeOutEnd() { divElem.style.display = "none"; divElem.removeEventListener("animationend", handleFadeOutEnd); }, { once: true }); } else { divElem.style.display = "block"; divElem.classList.remove("fade-out"); divElem.classList.add("fade-in"); } }
同时调整CSS关键帧,移除display相关代码:
@keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } }
这两种方案都能在三大浏览器中实现一致的淡入淡出效果,大家可以根据需求选择~
内容来源于stack exchange
相关产品推荐
相关产品推荐

