需求:现在需要两个表格,为了方便对比左右的数据,需要其中一边的表格滚动时,另一边的表格也跟着一起滚动,并且保持滚动位置的一致性。具体如下图所示。
实现步骤:
确保两个表格的宽度一致:如果两个表格的宽度不一致,可能会导致滚动条的位置不同步。确保两个表格的宽度相同(数据内容超出表格行的宽度可以省略显示)或根据需要调整。
使用相同的滚动容器:确保两个表格的滚动容器具有相同的高度,这样它们的滚动行为才会同步。
同步滚动事件:在两个表格上监听滚动事件,并在事件触发时同步另一个表格的滚动位置。
HTML结构:
创建一个包含两个表格的容器,然后给容器一个固定的高度(因为要实现滚动必须要内容超过高度了才会滚动)。
<template>
<div class="container-box">
<div class="flex table-box">
<!-- 左边表格 -->
<div class="left-table" ref="leftTableWrapper">
<el-table
:data="LeftTableData"
border
ref="leftTableRef"
style="width: 100%"
height="100%"
:fit="true"
header-row-class-name="header-row"
:cell-style="{
'font-size': '12px',
color: '#666666',
height: '40px',
}"
>
<template #empty>
<div class="custom-empty">
<el-empty
:image="`${$global.imageBaseUrl}/ledger/billProcessing/icon_nodata.png`"
description="暂无数据"
/>
</div>
</template>
<el-table-column
label="表头1"
prop="left1"
align="center"
min-width="150"
/>
<el-table-column
label="表头2"
prop="left2"
align="center"
width="100"
/>
</el-table>
</div>
<!-- 右边表格 -->
<div class="right-table" ref="rightTableWrapper">
<el-table
:data="rightTableData"
border
ref="rightTableRef"
style="width: 100%"
height="100%"
:fit="true"
header-row-class-name="header-row"
:cell-style="{
'font-size': '12px',
color: '#666666',
height: '40px',
}"
>
<template #empty>
<div class="custom-empty">
<el-empty
:image="`${$global.imageBaseUrl}/ledger/billProcessing/icon_nodata.png`"
description="暂无数据"
/>
</div>
</template>
<el-table-column
label="表头1"
prop="right1"
align="center"
min-width="150"
/>
<el-table-column
label="表头2"
prop="right2"
align="center"
width="100"
/>
</el-table>
</div>
</div>
</div>
</template>
JavaScript逻辑:
模拟左右表格的数据,初始设置100条。
在两个表格上监听滚动事件,并在事件触发时同步另一个表格的滚动位置。
使用
nextTick
确保DOM更新完成后再同步滚动位置。setupTableScrollSync 函数通过获取 el-table 的 body 容器,给左右表格的滚动事件绑定同步逻辑,避免循环触发(用 isSyncingScroll 标记)。
<script lang="ts" setup>
interface tableInfo {
left1?: string
left2?: string
right1?: string
right2?: string
}
let LeftTableData = ref<tableInfo[]>([])
let rightTableData = ref<tableInfo[]>([])
const leftTableRef = ref()
const rightTableRef = ref()
const leftTableWrapper = ref()
const rightTableWrapper = ref()
onMounted(() => {
for (let index = 0; index < 100; index++) {
let obj = {
left1: `表${index + 1}`,
left2: `表${index + 1}-1`,
}
let obj2 = {
right1: `表${index + 1}`,
right2: `表${index + 1}-1`,
}
LeftTableData.value.push(obj)
rightTableData.value.push(obj2)
}
// 表格滚动同步
nextTick(() => {
setupTableScrollSync()
})
})
// 滚动同步实现
let isSyncingScroll = false
function setupTableScrollSync() {
// 获取表格body的滚动容器
const getTableBodyWrapper = (tableRef: any) => {
// el-table exposes $el, then .querySelector('.el-scrollbar__wrap')
// 兼容 el-table 2.x/3.x
if (!tableRef?.value) return null
// 先找新版class
let body = tableRef.value.$el.querySelector('.el-scrollbar__wrap')
if (!body) {
// 旧版class
body = tableRef.value.$el.querySelector('.el-table__body-wrapper')
}
return body
}
const leftBody = getTableBodyWrapper(leftTableRef)
const rightBody = getTableBodyWrapper(rightTableRef)
if (!leftBody || !rightBody) return
// 解绑旧的监听,防止重复
leftBody.onscroll = null
rightBody.onscroll = null
leftBody.onscroll = (e: Event) => {
if (isSyncingScroll) return
isSyncingScroll = true
rightBody.scrollTop = leftBody.scrollTop
isSyncingScroll = false
}
rightBody.onscroll = (e: Event) => {
if (isSyncingScroll) return
isSyncingScroll = true
leftBody.scrollTop = rightBody.scrollTop
isSyncingScroll = false
}
}
</script>
CSS 样式:
<style lang="scss" scoped>
.container-box {
background-color: #fff;
border-radius: 4px;
margin: 16px;
height: calc(100vh - 100px);
padding: 24px;
.table-box {
height: calc(100% - 465px);
.left-table {
margin-right: 16px;
}
.left-table,
.right-table {
:deep(.header-row th) {
background-color: #f5f5f5;
text-align: center;
}
}
}
}
</style>
想要实现表格同步滚动,其实还有一个办法,就是用一个表格来实现(前提是数据是放在一个数组里的) ,然后中间想要空隙,通过CSS样式为表格的某些行或列添加间隔,这种方法实现简单,无需处理滚动逻辑。如果需要真正的固定列+滚动列效果,推荐用两个表格来实现;如果只是视觉分隔,一个表格来实现更简单。