Chrome浏览器中基于details/summary的JS动画手风琴异常排查
Chrome中details手风琴自动关闭已有展开项的原因
我实现了一个通过max-height控制展开/收起动画的details手风琴组件,代码如下:
JavaScript 代码
document.querySelectorAll('summary').forEach(el => el.addEventListener('click', (event) => { const details = event.target.parentElement; const onAnimationEnd = cb => details.addEventListener( "animationend", cb, { once: true } ); details.classList.add('expand'); if (details.open) { event.preventDefault(); details.classList.add('collapse'); onAnimationEnd(() => { details.classList.remove('expand', 'collapse'); details.removeAttribute("open"); }); setTimeout(() => { details.dispatchEvent(new Event('animationend')); }, 500); } }));
CSS 代码
details { max-height: 2rem; overflow: hidden; background-color: #eee; } details>p { margin-top: 0; } summary { min-height: 2rem; border-top: solid 1px #ccc; background-color: #fff; } .expand { overflow: hidden; max-height: 10rem; transition: max-height .5s ease-out; } .collapse { overflow: hidden; max-height: 2rem; transition: max-height .5s ease-out; }
HTML 代码
<details name="learn-css"> <summary>Accordion Summary 1</summary> <p>Accordion Content 1</p> </details> <details name="learn-css"> <summary>Accordion Summary 2</summary> <p>Accordion Content 2</p> </details> <details name="learn-css"> <summary>Accordion Summary 3</summary> <p>Accordion Content 3</p> </details> <div> <p>Text</p> </div>
问题现象
这段代码在Firefox中正常运行,但在Chrome里出现异常:当已有一个details处于展开状态时,点击另一个关闭的details将其打开,已展开的details会自动关闭,open属性被移除,导致CSS动画失效。我原本需要手动编写关闭逻辑,这种自动关闭是Chrome的意外副作用。
我尝试改用CSS keyframes实现动画(代码如下),调整JS逻辑,但核心问题依旧存在:
修改后的CSS代码
details { max-height: 2rem; overflow: hidden; background-color: #eee; } details[open] { max-height: 10rem; } details > p { margin-top: 0; } summary { min-height: 2rem; border-top: solid 1px #ccc; background-color: #fff; } .expand { animation-name: expand-height; animation-duration: .5s; animation-timing-function: ease-out; overflow: hidden; } .collapse { animation-name: collapse-height; animation-duration: .5s; animation-timing-function: ease-out; overflow: hidden; } @keyframes expand-height { 0% { max-height: 2rem; } 100% { max-height: 10rem; } } @keyframes collapse-height { 0% { max-height: 10rem; } 100% { max-height: 2rem; } }
修改后的JavaScript代码
document.querySelectorAll('summary').forEach(el => el.addEventListener('click', (event) => { const details = event.target.parentElement; const onAnimationEnd = cb => details.addEventListener( "animationend", cb, { once: true } ); details.classList.add('expand'); if (details.open) { event.preventDefault(); details.classList.add('collapse'); onAnimationEnd(() => { details.classList.remove('expand', 'collapse'); details.removeAttribute("open"); }); } }));
原因分析
问题根源在于所有details元素都设置了相同的name="learn-css"属性。
Chrome浏览器对带有相同name属性的<details>元素实现了「分组单选」的默认行为——将同name的details视为一组,点击展开其中一个时,浏览器会自动关闭同组内其他已展开的details,同时移除它们的open属性。而Firefox目前并未实现这一特性,因此出现了跨浏览器行为差异。
这种行为类似<input type="radio">的分组逻辑,是Chrome对details元素的扩展实现,并非HTML标准规定的行为,所以会导致你手动编写的动画和状态控制逻辑被浏览器默认行为打断,出现动画失效的情况。
内容的提问来源于stack exchange,提问作者gbecker
相关产品推荐
相关产品推荐

