使用原生display动画时,HTML dialog关闭时backdrop动画失效问题排查
我尝试用原生可动画display特性实现HTML dialog的开合动画,该特性仅支持Chrome、Edge和Opera。
注意:我知道可以通过添加关闭类配合onanimationend事件调用dialog.close()实现动画,但我现在只关注让原生display none动画正常工作,不考虑该方案。
目前dialog本身的开合动画正常,打开时::backdrop的动画也能执行,但关闭时::backdrop的动画完全没效果。我试过给backdrop单独写不含display的淡出关键帧,还是没用。怀疑是离散动画只作用于元素本身,不影响伪元素,但不确定。
代码片段
JavaScript
const dialog = document.getElementById("dialog") const open = document.getElementById("open") const close = document.getElementById("close") open.onclick = () => dialog.showModal() close.onclick = () => dialog.close()
CSS
dialog { background: white; color: black; padding: 1rem; border: unset; inset: unset; width: unset; height: unset; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 0.5rem 0 rgba(0,0,0,0.2); animation: scale-out 1000ms forwards, fade-out 1000ms forwards; } dialog::backdrop { background: rgba(0,0,0,0.5); animation: test 1000ms forwards; } dialog[open] { animation: scale-in 1000ms forwards, fade-in 1000ms forwards; } dialog[open]::backdrop { animation: fade-in 1000ms forwards; } @keyframes test { 0% { opacity: 1; } 100% { opacity: 0; } } @keyframes fade-out { 0% { opacity: 1; display: block; } 100% { opacity: 0; display: none; } } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes scale-in { 0% { transform: translate(-50%, -50%) scale(0.5); } 100% { transform: translate(-50%, -50%) scale(1); } } @keyframes scale-out { 0% { transform: translate(-50%, -50%) scale(1); display: block; } 100% { transform: translate(-50%, -50%) scale(0.5); display: none; } }
HTML
<dialog id="dialog"> <button id="close">Close</button> </dialog> <button id="open">Open</button>
原因分析
问题核心在于:当调用dialog.close()时,浏览器会立即移除[open]属性,同时同步销毁::backdrop伪元素,导致为它定义的关闭动画根本没有机会执行——伪元素已经不存在了,自然无法播放动画。
原生可动画display特性仅作用于dialog元素本身,浏览器会延迟将display设置为none直到动画结束,但这个逻辑不会延伸到::backdrop伪元素。
解决方案(基于原生display动画特性)
要让::backdrop在关闭时也能播放动画,需要借助dialog的transition-behavior: allow-discrete属性(和可动画display配套的特性),同时调整CSS逻辑,让backdrop的动画依赖dialog的状态变化,而非直接依赖[open]属性:
修改后的CSS关键部分:
dialog { /* 原有样式保留 */ animation: scale-out 1000ms forwards, fade-out 1000ms forwards; /* 添加这行,允许离散属性动画 */ transition-behavior: allow-discrete; } /* 用:not([open])替代默认状态,确保关闭时backdrop仍能被选中直到动画结束 */ dialog:not([open])::backdrop { background: rgba(0,0,0,0.5); animation: test 1000ms forwards; /* 同样添加允许离散动画 */ transition-behavior: allow-discrete; } dialog[open] { animation: scale-in 1000ms forwards, fade-in 1000ms forwards; } dialog[open]::backdrop { animation: fade-in 1000ms forwards; }
另外,需要确保dialog的关闭动画中display: none的设置是在动画最后帧,浏览器会自动延迟应用这个属性,让backdrop的动画有足够时间执行。
补充:这个方案仍依赖Chrome/Edge/Opera的支持,因为
transition-behavior和可动画display都是这些浏览器独有的特性。
内容的提问来源于stack exchange,提问作者Oscar

