携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
需求背景:
el-table组件的横向滚动条默认是放在table元素的最底下,当table高度足够高的时候,滚动条的位置会低于浏览器窗口。这时如果想要进行横向滚动就需要将浏览器窗口滚到下面。在进行横向滚动之后想要看的数据又在列表的第一条。这时有需要回到上面重新查看。这一来一回的操作显得有些冗余,效率不高。
最终出现在这个需求,希望滚动条对于table表格粘性贴底。
方案分析:
本需求使用到的环境是:element plus,vue3。
搜索之后有找到几个解决方案,但是都不太适用于本需求的场景 例如:
- 手写一个滚动条绑定table滚动条,然后手写的滚动条吸底(经实践,滚动条的绑定机制过于繁琐并且不方便进行大范围使用);
- 第三方组件el-table-horizontal-scroll(经实践该组件只支持vue2);
- el-table提供的max-height(实际页面情况很难计算max-height实际应该取什么值)
最终经研究得出的方案:
在DOM元素加载完成的时候,“剪取”el-table内的滚动条DOM元素模块,“粘贴”到el-table层级下(原本层级在el-table下的多个子层级)然后使它对于el-table元素进行粘性布局。
position: sticky的使用是有一定要求的。它的基本逻辑是,使用了position: sticky的元素,会向上寻找最近的滚动组件并根据它进行固定。滚动条的元素“剪取”到el-table元素的直属子层级,方便让滚动条元素“固定”到table上。
如果它中途的父级元素存在,overflow:hidden/scroll/auto/overlay时,sticky的行为将会被抑制。得不到页面效果
粘性布局的使用需要基础逻辑demo:
<template>
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
border
scrollbar-always-on
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="name" label="Name" width="180" />
</el-table>
</template>
<script>
import { reactive, toRefs, onMounted, ref } from "vue";
export default {
name: "TableList",
setup() {
let state = reactive({
tableRef: ref(),
});
const tableData = new Array(50).fill({ date: '2022-08-23', name: "pinklin" });
onMounted(() => {
if (state?.tableRef) {
const bar = state.tableRef.$el?.querySelector(".el-scrollbar__bar");
state.tableRef.$el.appendChild(bar);
}
});
return {
...toRefs(state),
tableData,
};
},
computed: {},
};
</script>
<style>
.el-scrollbar__bar {
z-index: 11;
overflow-x: hidden;
position: sticky;
}
.el-table.el-table--border {
overflow: unset;
}
</style>
为了方便使用。我们把它封装到vue3的指令里面。使用的时候只需要将其引入,然后在el-table上使用v-scrollbar即可
具体指令的实现如下:
/**
* v-scrollbar
* 放在el-table 标签上 使其底部滚动条吸底
* 接收参数:Boolean
* v-scrollbar="true"
*/
import type { Directive, DirectiveBinding } from 'vue';
const directive: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
if (binding.value) {
const bar = el?.querySelector('.el-scrollbar__bar');
if (bar && el) {
bar?.classList?.add('v-scroll__el-scrollbar__bar');
el?.appendChild(bar);
}
}
},
};
export default directive;
同时,把css样式设置到全局,或者放到使用到该指令的地方。
.v-scroll__el-scrollbar__bar.el-scrollbar__bar{
z-index: 11;
overflow-x: hidden;
position: sticky;
}
.el-table.el-table--border{
overflow: unset;
}
最终实现的效果大致如下如图: