【问题】表格横向太长,横向滚动条需滚动至表格最底部才能拖动,不便于浏览。
【需求】基于elment的el-table
组件生成的表格,使其可以横向拖拽滚动。
要解决这个问题,首先需要知道横向滚动如何实现。
JS中滚动相关的属性包括:scrollWidth、scrollHeight、scrollLeft 、scrollTop
。
【条件】 元素可滚动的前提条件是元素的宽度或高度超出给定区域,且开启了滚动条。
element.scrollWidth
:返回元素的整体宽度,包括由于溢出而无法展示在网页的不可见部分。element.scrollLeft
:返回元素的整体高度,包括由于溢出而无法展示在网页的不可见部分。element.scrollLeft
:返回元素左边缘与视图之间的距离,这里的视图指的是元素的内容(包括子元素以及内容)。element.scrollHeight
:返回元素上边缘与视图之间的距离。【拓展】JS中相关概念有很多,包括
client系列,offset系列,scroll系列
,详细解释可参考https://juejin.cn/post/7116306912198524959#heading-22
【注意】滚动时改变的是父元素的scrollLeft/scrollHeight。
以scrollLeft为例,初始值为0,此时子元素最左端与父元素最左端重合;滚动条右滑,子元素向左移动,父元素的scrollLeft为正值。
【例】 父元素宽度200px,子元素宽度400px,有溢出情况,开启父元素的横向滚动条overflow-x:scroll;
,代码如下:
<style>
.father{
width: 200px;
height: 200px;
border: 2px solid;
overflow-x: scroll;
}
.son{
width: 400px;
height: 150px;
background-color: pink;
}
style>
<body>
<div class="father">
<div class="son">sonsonsonsonsonsonsonsonsondiv>
div>
body>
<script>
const father = document.querySelector(".father");
console.log(father.scrollLeft)
father.addEventListener('scroll',function(e){
console.log(e.target.scrollLeft)
})
script>
起始打印父元素的scrollLeft结果为0,向右滚动后,打印父元素的scrollLeft结果为具体数值,且为正值。
【总结】 因此,横向滚动的关键在于找到溢出元素的父元素,并通过改变其scrollLeft
值实现横向滚动。
由于elment组件进行了很多包装,所以较难定位目标元素,我们可以先找到el-table元素,然后逐步寻找。
这里使用element-plus和vue3写一个简单的例子。tableData有6个属性,显然这里没显示完整,并出现了滚动条。
首先拿到这个table元素:给el-table添加ref,并拿到元素。
<template>
....
<el-table :data="tableData" style="width: 100%" border ref="tableRef">
....
el-table>
....
template>
<script setup>
const tableRef = ref(null);
console.dir(tableRef);
script>
先打印一下这个元素看看,可以看到有很多属性。
我们的目的是拿到包裹表格的父元素,参考这篇文章利用 bodyWrapper 实现表格自动滚动,了解到可以利用bodyWrapper这个元素,Vue3中获取方式为tableRef.value.$refs.bodyWrapper
,按照文章中的方法我们添加一个按钮,点击的时候让表格向左滚动,代码如下:
<template>
....
<el-table :data="tableData" style="width: 100%" border ref="tableRef">
....
el-table>
<el-button type="primary" @click="handleClick">右移el-button>
....
template>
<script setup>
const tableRef = ref(null);
const handleClick = () => {
const tableBody = tableRef.value.$refs.bodyWrapper;
tableBody.scrollLeft += 20;
};
script>
发现并不起作用,打印tableBody,发现其scrollLeft没有改变,一直是0。说明我们找的元素不对,这个元素不是包裹table的第一父元素。
顺着这个思路,打印tableRef.value.$refs
看看上面有哪些元素。
然后我们发现.el-table__body
其实是藏在.el-scrollbar
下面的。
然后查看这些元素的样式,找到包裹table的第一层父元素。可以看到.el-scrollbar__view
包裹的元素宽度是整个表格的宽度,.el-scrollbar__wrap
包裹的元素宽度是父元素的宽度。
.el-scrollbar__view
就是子元素,宽度有溢出,.el-scrollbar__wrap
就是父元素,可以横向滚动。
因此我们要找的目标元素就是 .el-scrollbar__wrap
包裹的元素。
对应的获取代码为:tableDataRef.value.$refs.scrollBarRef.wrapRef
。
回到我们的测试demo,打印这个元素的ScrollLeft。点击按钮,发现ScrollLeft值已经发生改变,表格也发生了移动。
const tableRef = ref(null);
const handleClick = () => {
const tableBody = tableRef.value.$refs.scrollBarRef.wrapRef;
tableBody.scrollLeft += 20;
console.log(tableBody.scrollLeft);
};
【总结】 要找的目标元素必须是包裹table的第一父元素, .el-scrollbar__wrap
包裹的元素就是我们要找的目标元素。
那么接下来就简单了,只需要捕获鼠标事件并给目标元素的scrollLeft赋值就可以实现横向滚动。
涉及到的鼠标事件包括:mousedown,mouseup,mousemove
,给表格元素添加相应的鼠标事件。
【mousedown】 鼠标按下时需要:1.可拖拽状态改为允许拖拽。2.记录鼠标位置。3.鼠标样式改为小手。
element.addEventListener('mousedown', (e) => {
// 拿到目标元素
tableBody = tableDataRef.value.$refs.scrollBarRef.wrapRef;
// 拖拽状态改为允许拖拽
mouseFlag = true;
// 记录鼠标按下位置
mouseStart = e.clientX;
// 记录元素当前scrollLeft值
startX = tableBody.scrollLeft;
// 鼠标样式修改
tableBody.style.cursor = 'grab';
});
【mousemove】 鼠标移动时需要:1.判断是否可拖拽。2.允许拖拽时记录鼠标移动距离。3.修改目标元素scrollLeft值。
element.addEventListener('mousemove', (e) => {
if (mouseFlag) {
// 记录鼠标移动距离
let offset = e.clientX - mouseStart;
// 修改目标元素scrollLeft值
tableBody.scrollLeft = startX - offset;
}
});
【mouseup】 鼠标抬起时需要:1.可拖拽状态改为禁止拖拽。2.鼠标样式恢复。
element.addEventListener('mouseup', (e) => {
mouseFlag = false;
tableBody.style.cursor = 'auto';
});
整理成一个统一的函数dragTable,每次仅需要传入需要进行拖拽的表格的dom。
// tableDataRef表示需要进行拖拽的表格的dom
export const dragTable = (tableDataRef) => {
let mouseFlag = false;
let mouseStart = 0;
let startX = 0;
let tableBody = null;
tableDataRef.value.$el.addEventListener('mousedown', (e) => {
tableBody = tableDataRef.value.$refs.scrollBarRef.wrapRef;
mouseFlag = true;
mouseStart = e.clientX;
startX = tableBody.scrollLeft;
tableBody.style.cursor = 'grab';
});
tableDataRef.value.$el.addEventListener('mouseup', () => {
mouseFlag = false;
tableBody.style.cursor = 'auto';
});
tableDataRef.value.$el.addEventListener('mousemove', (e) => {
if (mouseFlag) {
let offset = e.clientX - mouseStart;
tableBody.scrollLeft = startX - offset;
}
});
};
用手机简单录制了一个实现效果,有点模糊,可以凑合看看哈哈