🌲系列一:跟着官方示例学习 @tanStack-table --- Basic
🌲系列二:跟着官方示例学习 @tanStack-table --- Header Groups
🌲系列三:跟着官方示例学习 @tanStack-table --- Column Filters
🌲系列四:跟着官方示例学习 @tanStack-table --- Column Ordering
🧠 什么是 Sticky Pinning?
Sticky Pinning
让表格的列在横向滚动时保持固定在左侧或右侧,这一功能非常实用,尤其是在展示大量列数据时,可以让用户始终看到关键字段,比如姓名或操作按钮。就像 Excel 的“冻结窗格”一样。
这是通过结合 position: sticky
和 left/right
等样式实现的,而不是传统的 position: fixed
,因此更自然地嵌入表格布局中。
🛠 如何实现 Sticky Pinning?
✅ 第一步:启用列 Pinning 功能
TanStack Table
提供了一套 pinning API
,不过需要自己定义交互和样式。官方并没有内置样式,但我们可以使用如下样式函数来统一管理:
const getCommonPinningStyles = (column: Column<Person>): CSSProperties => {
const isPinned = column.getIsPinned();
const isLastLeftPinnedColumn =
isPinned === "left" && column.getIsLastColumn("left");
const isFirstRightPinnedColumn =
isPinned === "right" && column.getIsFirstColumn("right");
return {
boxShadow: isLastLeftPinnedColumn
? "-4px 0 4px -4px gray inset"
: isFirstRightPinnedColumn
? "4px 0 4px -4px gray inset"
: undefined,
left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
opacity: isPinned ? 0.95 : 1,
position: isPinned ? "sticky" : "relative",
width: column.getSize(),
zIndex: isPinned ? 1 : 0,
};
};
column.getIsPinned()
获取当前列是否被Pin
(固定)isLastLeftPinnedColumn
和isFirstRightPinnedColumn
判断是否是“最后一个左固定列”或“第一个右固定列”,这些判断用于添加分隔视觉效果(如阴影box-shadow
)。如果有多个左/右固定列,不希望它们每一个都加阴影,只需加在“最左/右边那个”上。getStart("left")
返回从表格左边到当前列开始位置的总宽度。getAfter("right")
返回当前列之后所有右固定列的宽度。这两个值动态决定sticky
的偏移量,确保列对齐。position: isPinned ? "sticky" : "relative"
!!! 最重要的是:固定列用position: sticky
,其余用默认的relative
🧪 添加 Pin 按钮交互
使用 column.pin()
方法将列固定到 "left"
、"right"
,或使用 false
取消固定。
{header.column.getIsPinned() !== "left" && (
<button onClick={() => header.column.pin("left")}>{"<="}</button>
)}
{header.column.getIsPinned() && (
<button onClick={() => header.column.pin(false)}>X</button>
)}
{header.column.getIsPinned() !== "right" && (
<button onClick={() => header.column.pin("right")}>{"=>"}</button>
)}
💻 实战示例
结合 TanStack Table
的 API
和 getCommonPinningStyles
函数,渲染 thead
和 tbody
中的单元格:
<th style={{ ...getCommonPinningStyles(column) }}>
{flexRender(header.column.columnDef.header, header.getContext())}
</th>
<td style={{ ...getCommonPinningStyles(column) }}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
💡 border-collapse: separate
💡 注意:
在
getCommonPinningStyles
上方的注释中,最后一行标注
View the index.css file for more needed styles such as border-collapse: separate
你需要保证
<table>
元素使用了border-collapse: separate
,以防止sticky
被破坏。
查阅官方的源码中 index.css
文件
table {
/* box-shadow and borders will not work with positon: sticky otherwise */
border-collapse: separate !important;
border-spacing: 0;
}
作用:让
position: sticky
和box-shadow
等样式在<td>/<th>
上生效
默认情况下,HTML <table>
使用的是 border-collapse: collapse
,这会让所有 <td>
和 <th>
之间的边框合并。这种合并有两个问题:
它会让单元格之间没有“独立的视觉盒子(box)”,从而导致 position: sticky
和 box-shadow
在某些浏览器中无效或表现异常。
所有单元格行为都被 table
主体“吞掉”,无法形成单独的 sticky
层级。
✅ 所以必须强制设为 border-collapse: separate
,让每个 cell
成为 独立可控制的盒子,从而支持:
-
position: sticky
-
z-index
-
box-shadow
-
overflow: hidden
等高级样式
❗️加上
!important
是为了覆盖某些浏览器或组件库中对<table>
的默认样式设置。
🔔 对官方示例代码可能存在一些删减的情况
代码地址🔗:Gitee
官方代码地址🔗: @tanStack/react-table