切换display属性时CSS transition失效问题咨询
问题原因与解决方案
为什么切换display属性会导致transition失效?
CSS过渡(transition)仅对有连续数值变化的可动画属性生效,而display: none是一个「二元切换」属性——元素要么完全脱离页面渲染树(不可见),要么完全参与渲染(可见),没有任何中间状态。
当你在JS中同时执行两个操作:
- 移除tr的
d-none类(将display从none改为table-row) - 给div添加
expanded类(将height从0改为200px)
浏览器会将这两个操作合并为一次重绘,直接让元素从「不可见」状态跳到「height:200px可见」状态,完全跳过了transition需要的从0到200px的渐变过程,因此过渡动画失效。
而移除d-none逻辑后,tr始终处于渲染状态,div的height变化是连续的数值过渡,符合transition的触发条件,所以动画能正常运行。
解决方案
方案1:用height+overflow替代display:none隐藏元素
通过设置tr的height:0和overflow:hidden实现默认隐藏,避免使用display:none,同时保留过渡效果:
修改后的CSS
/* 控制tr的显示/隐藏过渡 */ .inventory-row { height: 0; overflow: hidden; transition: height 0.3s linear; } .inventory-row.expanded { height: 200px; /* 与内部div的高度匹配 */ } /* 内部div的过渡保持不变 */ .inventory { height: 0px; max-height: 200px; overflow-y: auto; transition: height .3s linear; } .inventory.expanded{ height: 200px; }
修改后的HTML
<tr class="data-row"> ... <td class="mb-0 p-1 text-end" style="height: 36px"><h6 class="text-sm mb-0"><em class="table-icon fas fa-chevron-down text-primary cursor-pointer" onclick="expand_card(this)" id="" aria-hidden="true"></em></h6></td> </tr> <!-- 移除d-none,添加inventory-row类 --> <tr class="mb-0 p-1 inventory-row" id="inventory-table-"> <td class="bg-gray-100 py-0 px-2" id="" colspan="8" style="vertical-align: top;"><div class="inventory" style="" id="inventory-div-"></div></td> </tr>
修改后的JS
function expand_card(e) { let open = e.classList.contains("open"); const inventoryRow = document.getElementById('inventory-table-' + e.id); const inventoryDiv = document.getElementById('inventory-div-' + e.id); if (open) { inventoryRow.classList.remove('expanded'); inventoryDiv.classList.remove('expanded'); cards_inventory_table_delete(e.id); e.classList.remove("open"); } else { inventoryRow.classList.add('expanded'); inventoryDiv.classList.add('expanded'); cards_read(e.id); e.classList.add("open"); } }
方案2:延迟触发div的过渡动画
如果必须保留display:none,可以通过requestAnimationFrame让浏览器先完成tr的显示渲染,再触发div的过渡:
修改后的JS
function expand_card(e) { let open = e.classList.contains("open"); const inventoryTable = document.getElementById('inventory-table-' + e.id); const inventoryDiv = document.getElementById('inventory-div-' + e.id); if (open) { // 先收起div,等过渡结束后再隐藏tr inventoryDiv.classList.remove('expanded'); setTimeout(() => { inventoryTable.classList.add('d-none'); }, 300); // 时间与transition的duration一致 cards_inventory_table_delete(e.id); e.classList.remove("open"); } else { // 先显示tr,再延迟触发div的过渡 inventoryTable.classList.remove('d-none'); requestAnimationFrame(() => { inventoryDiv.classList.add('expanded'); }); cards_read(e.id); e.classList.add("open"); } }
内容的提问来源于stack exchange,提问作者Ross
相关产品推荐
相关产品推荐

