时隔多日,再次遇到值得记录的问题。
需求
项目前端使用vue框架,页面使用element-ui进行页面快速搭建。默认的table组件当表格过长时,下方会出现横向的滚动条,便于用户对表格进行左右滑动。考虑到页面美观问题,滚动条设置的很窄,导致用户使用时不方便进行左右滑动。
现要求,去除表格下方滚动条,用户可直接拖拽表格实现左右滑动功能。
表格设置固定表头和列,实践证明并不影响此功能。
思路
鼠标点击进行拖拽,首先想到鼠标的点击事件,添加mousedown
、mouseleave
、mouseup
和mousemove
事件的监听器,实现拖拽效果。通过设置tableBodyWrapper.style.overflow = 'hidden';
隐藏原生的滚动条。
实现
要实现拖拽功能,并确保 tableBodyWrapper
可以正确拖拽,需要设置事件监听器和对样式进行一些调整。下面是实现代码:
<template>
<div ref="tableContainer" class="table-container">
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
};
},
mounted() {
this.enableDrag();
},
methods: {
enableDrag() {
this.$nextTick(() => {
const tableContainer = this.$refs.tableContainer;
const tableBodyWrapper = this.$refs.table.$el.querySelector('.el-table__body-wrapper');
if (!tableBodyWrapper) {
console.error('找不到表体。');
return;
}
let isDown = false;
let startX, scrollLeft;
tableBodyWrapper.addEventListener('mousedown', (e) => {
isDown = true;
startX = e.pageX - tableBodyWrapper.offsetLeft;
scrollLeft = tableBodyWrapper.scrollLeft;
tableBodyWrapper.style.cursor = 'grabbing';
});
tableBodyWrapper.addEventListener('mouseleave', () => {
isDown = false;
tableBodyWrapper.style.cursor = 'grab';
});
tableBodyWrapper.addEventListener('mouseup', () => {
isDown = false;
tableBodyWrapper.style.cursor = 'grab';
});
tableBodyWrapper.addEventListener('mousemove', (e) => {
if (!isDown) return;
e.preventDefault();
const x = e.pageX - tableBodyWrapper.offsetLeft;
const walk = (x - startX) * 2; // scroll-fast
tableBodyWrapper.scrollLeft = scrollLeft - walk;
});
// 隐藏滚动条
tableBodyWrapper.style.overflowX = 'hidden';
});
}
}
};
</script>
<style>
.table-container {
overflow: hidden;
white-space: nowrap;
}
.el-table__body-wrapper {
cursor: grab;
}
.el-table__body-wrapper:active {
cursor: grabbing;
}
</style>
解释:
- 获取DOM元素:在
this.$nextTick()
回调中,通过this.$refs.table.$el.querySelector('.el-table__body-wrapper')
获取到实际的表格内容区域的DOM元素。这样就确保我们在对DOM元素进行操作,而不是组件实例。 - 检查 DOM 元素存在:在
this.$nextTick
中,我们先检查tableElement
是否存在,然后再查询tableBodyWrapper
。 - 添加错误处理:如果 tableBodyWrapper 没有找到,输出错误信息到控制台。这有助于调试并确保代码的稳健性。
- 拖拽事件绑定到
tableBodyWrapper
:确保拖拽事件绑定在实际可滚动的tableBodyWrapper
上。 - 样式调整:使用
tableBodyWrapper
的样式来显示抓手光标,并在拖动时切换光标样式。 - 隐藏水平滚动条:通过设置
overflowX: hidden
来隐藏原生滚动条,但确保滚动功能仍然有效。
更新兼容手机拖拽功能
因之前代码只对pc端进行实现,手机进行拖拽无反应,现新加入手机拖拽事件。以下是修改后的代码,添加了触摸事件的支持:
<div ref="tableContainer" class="table-container">
<el-table ref="table">
</el-table>
</div>
methods: {
enableDrag () {
this.$nextTick(() => {
const tableContainer = this.$refs.tableContainer;
const tableBodyWrapper = this.$refs.table.$el.querySelector('.el-table__body-wrapper');
if (!tableBodyWrapper) {
console.error('Table body wrapper not found.');
return;
}
let isDown = false;
let startX, scrollLeft;
// 鼠标事件
tableBodyWrapper.addEventListener('mousedown', (e) => {
isDown = true;
startX = e.pageX - tableBodyWrapper.offsetLeft;
scrollLeft = tableBodyWrapper.scrollLeft;
tableBodyWrapper.style.cursor = 'grabbing';
});
tableBodyWrapper.addEventListener('mouseleave', () => {
isDown = false;
tableBodyWrapper.style.cursor = 'grab';
});
tableBodyWrapper.addEventListener('mouseup', () => {
isDown = false;
tableBodyWrapper.style.cursor = 'grab';
});
tableBodyWrapper.addEventListener('mousemove', (e) => {
if (!isDown) return;
e.preventDefault();
const x = e.pageX - tableBodyWrapper.offsetLeft;
const walk = (x - startX) * 2; // scroll-fast
tableBodyWrapper.scrollLeft = scrollLeft - walk;
});
// 触摸事件
tableBodyWrapper.addEventListener('touchstart', (e) => {
isDown = true;
startX = e.touches[0].pageX - tableBodyWrapper.offsetLeft;
scrollLeft = tableBodyWrapper.scrollLeft;
});
tableBodyWrapper.addEventListener('touchend', () => {
isDown = false;
});
tableBodyWrapper.addEventListener('touchmove', (e) => {
if (!isDown) return;
e.preventDefault();
const x = e.touches[0].pageX - tableBodyWrapper.offsetLeft;
const walk = (x - startX) * 2; // scroll-fast
tableBodyWrapper.scrollLeft = scrollLeft - walk;
});
// 隐藏滚动条
tableBodyWrapper.style.overflowX = 'hidden';
});
}
}
在这个代码中,我们为 touchstart
, touchend
和 touchmove
事件添加了相应的处理函数,以支持在手机上的左右拖拽操作。这样既兼容了PC上的鼠标拖拽,也支持了手机上的触摸拖拽。