如何在React(及MUI)中实现可展开/折叠列的表格?
实现表格列展开/折叠的平滑动画方案
普通HTML + CSS 实现方案
核心思路是利用CSS max-width过渡动画控制子列的显示/隐藏,同时通过固定表格布局保证列对齐。
代码示例
<!DOCTYPE html> <html lang="zh-CN"> <head> <style> table { width: 100%; border-collapse: collapse; table-layout: fixed; /* 固定布局避免列偏移 */ } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } .main-column { cursor: pointer; position: relative; } .main-column::after { content: ' ▶'; position: absolute; right: 8px; transition: transform 0.3s ease; } .table-expanded .main-column::after { transform: rotate(90deg); } .sub-column { max-width: 0; overflow: hidden; transition: max-width 0.5s ease; white-space: nowrap; /* 防止内容换行干扰动画 */ } .table-expanded .sub-column { max-width: 200px; /* 根据实际内容调整 */ } </style> </head> <body> <table id="expandable-table"> <thead> <tr> <th class="main-column">主列1</th> <th class="sub-column">子列1-1</th> <th class="sub-column">子列1-2</th> <th>普通列</th> </tr> </thead> <tbody> <tr> <td class="main-column">行1主内容</td> <td class="sub-column">行1子内容1</td> <td class="sub-column">行1子内容2</td> <td>行1普通内容</td> </tr> <tr> <td class="main-column">行2主内容</td> <td class="sub-column">行2子内容1</td> <td class="sub-column">行2子内容2</td> <td>行2普通内容</td> </tr> </tbody> </table> <script> const mainHead = document.querySelector('.main-column'); const table = document.getElementById('expandable-table'); mainHead.addEventListener('click', () => { table.classList.toggle('table-expanded'); }); </script> </body> </html>
关键细节
table-layout: fixed强制表格列宽稳定,避免内容变化导致列对齐混乱- 子列通过
max-width从0过渡到目标值,配合overflow: hidden实现平滑展开/折叠 - 主列表头添加箭头图标,点击时旋转提示状态变化
- 所有行的子列通过同一个表格类控制,实现同步展开/折叠
React + Material UI 优化方案
之前使用Collapse组件出现问题,是因为它针对垂直高度过渡,而列需要水平宽度过渡。改用自定义样式实现:
代码示例
import { useState } from 'react'; import { Table, TableHead, TableBody, TableRow, TableCell, styled } from '@mui/material'; // 自定义子列样式 const StyledSubCell = styled(TableCell)(({ theme }) => ({ maxWidth: 0, overflow: 'hidden', transition: 'max-width 0.5s ease', whiteSpace: 'nowrap', '&.expanded': { maxWidth: 200, // 根据内容调整 }, })); // 自定义主列表头样式 const StyledMainHeadCell = styled(TableCell)(({ theme }) => ({ cursor: 'pointer', position: 'relative', '&::after': { content: "' ▶'", position: 'absolute', right: theme.spacing(1), transition: 'transform 0.3s ease', }, '&.expanded::after': { transform: 'rotate(90deg)', }, })); export default function ExpandableColumnTable() { const [isExpanded, setIsExpanded] = useState(false); const handleToggle = () => { setIsExpanded(!isExpanded); }; return ( <Table sx={{ tableLayout: 'fixed', width: '100%' }}> <TableHead> <TableRow> <StyledMainHeadCell onClick={handleToggle} className={isExpanded ? 'expanded' : ''} > 主列1 </StyledMainHeadCell> <StyledSubCell className={isExpanded ? 'expanded' : ''}>子列1-1</StyledSubCell> <StyledSubCell className={isExpanded ? 'expanded' : ''}>子列1-2</StyledSubCell> <TableCell>普通列</TableCell> </TableRow> </TableHead> <TableBody> <TableRow> <TableCell>行1主内容</TableCell> <StyledSubCell className={isExpanded ? 'expanded' : ''}>行1子内容1</StyledSubCell> <StyledSubCell className={isExpanded ? 'expanded' : ''}>行1子内容2</StyledSubCell> <TableCell>行1普通内容</TableCell> </TableRow> <TableRow> <TableCell>行2主内容</TableCell> <StyledSubCell className={isExpanded ? 'expanded' : ''}>行2子内容1</StyledSubCell> <StyledSubCell className={isExpanded ? 'expanded' : ''}>行2子内容2</StyledSubCell> <TableCell>行2普通内容</TableCell> </TableRow> </TableBody> </Table> ); }
关键优化点
- 放弃
Collapse组件,改用水平宽度过渡动画,避免垂直动画带来的对齐问题 - 给MUI表格设置
tableLayout: 'fixed',确保列宽稳定 - 通过自定义样式组件统一控制子列和主列的状态与动画
- 点击主列表头切换状态,同步控制所有子列的展开/折叠
通用注意事项
- 子列的
max-width值需根据实际内容长度调整,确保内容完全显示 - 若子列内容过多,可添加
text-overflow: ellipsis在折叠状态下显示省略号 - 如需支持多组主列分别控制不同子列,可给子列添加不同类名,通过点击对应主列切换状态
内容的提问来源于stack exchange,提问作者Ilann Ohayon
相关产品推荐
相关产品推荐

