实现效果
当列表内容部分可见时,保持横向滚动条在下方。
实现
原理
这里涉及到两个Dom元素,类名分别为 el-table__body-wrapper
、el-table__body
通过观察发现横向滚动条在于el-table__body-wrapper
上,el-table__body
则是实际的列表内容,当el-table__body
宽度超出el-table__body-wrapper
时就会出现横向滚动条。
因此只需要动态的修改el-table__body-wrapper
的height
即可实现想要的效果。
在页面发生滚动、页面大小改变以及数据源更新的时候对el-table__body-wrapper
的height
进行调整。
代码
这里是通过mixin方式的实现
let el;
let tableBodyWrapDom;
let tableBodyDom;
function handle() {
if (!el) return;
// top为dom上侧距离可视窗口顶部的值
const { top: tableBodyDomTop } = tableBodyWrapDom.getBoundingClientRect();
if (tableBodyDomTop > window.innerHeight || tableBodyWrapDom.classList.contains('is-scrolling-none')) {
// 此时列表在可视窗口的下侧不可见区域,因此不做任何修改
tableBodyWrapDom.style.height = 'unset'
tableBodyWrapDom.style.marginBottom = 'unset';
} else {
// 窗口高度 - 列表距顶部值 且 不超过自身实际值
let wrapHeight = Math.min(window.innerHeight - tableBodyDomTop, tableBodyDom.offsetHeight);
tableBodyWrapDom.style.height = wrapHeight + 'px';
// 需要用marginBottom填充,以保持列表原有高度,避免页面的纵向滚动条变化导致页面滚动的不流畅
// 可以通过注释这一行代码观察其效果差异
tableBodyWrapDom.style.marginBottom = (tableBodyDom.offsetHeight - wrapHeight) + 'px';
}
}
export default {
mounted() {
el = this.$el; // 当前组件的Dom对象,避免操作到本组件之外的Dom
tableBodyWrapDom = el.querySelector('.el-table__body-wrapper');
tableBodyDom = el.querySelector('.el-table__body');
// 监听事件
document.addEventListener('scroll', handle);
window.addEventListener('resize', handle);
},
destroyed() {
// 在组件销毁时取消监听
document.removeEventListener('scroll', handle);
window.removeEventListener('resize', handle);
},
watch: {
_list() {
// 当列表数据源发生变化时,再次触发
this.$nextTick(handle);
}
}
}
2021-2-25 更新 - 一个bug的解决
问题
当鼠标悬停在固定列上滚动滚轮时,会导致左侧内容发生滚动。
原因
查了许久,最终阅读了 el-table 源码发现了一段代码。
这个是固定列html模板的部分代码,主要是这个 =》 v-mousewheel="handleFixedMousewheel"
<div
v-if="rightFixedColumns.length > 0"
v-mousewheel="handleFixedMousewheel"
class="el-table__fixed-right"
ref="rightFixedWrapper"
:style="[{
width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '',
right: layout.scrollY ? (border ? layout.gutterWidth : (layout.gutterWidth || 0)) + 'px' : ''
},
fixedHeight]">
<div v-if="showHeader"
class="el-table__fixed-header-wrapper"
handleFixedMousewheel
的具体内容,粗看下来确实与发现的问题相符
handleFixedMousewheel(event, data) {
const bodyWrapper = this.bodyWrapper;
if (Math.abs(data.spinY) > 0) {
const currentScrollTop = bodyWrapper.scrollTop;
if (data.pixelY < 0 && currentScrollTop !== 0) {
event.preventDefault();
}
if (data.pixelY > 0 && bodyWrapper.scrollHeight - bodyWrapper.clientHeight > currentScrollTop) {
event.preventDefault();
}
bodyWrapper.scrollTop += Math.ceil(data.pixelY / 5);
} else {
bodyWrapper.scrollLeft += Math.ceil(data.pixelX / 5);
}
},
解决
简单粗暴,在mounted
生命周期中覆盖方法。
这里ref
的用法不清楚的直接看vue官方文档吧。
this.$refs.table.handleFixedMousewheel = function() {};
2021-12-3 更新
看了评论区有很多人反馈无效。。我这里自己测试是有效的,不过有一点需要关注
这里个滚动事件监听对象需要根据各自情况改变,本意就是在滚动时调用scrollBarFixedHandle方法,重新计算各数值。
document.addEventListener("scroll", this.scrollBarFixedHandle);
这里附一个codepen
https://codepen.io/lazysleep/pen/XWebOxP