【问题】表格横向太长,表格横向滚动条位于最底部,需将页面滚动至最底部才可左右拖动表格,用户体验感不好
【需求】基于elment的
el-table
组件生成的表格,使其可以横向拖拽滚动【实现】灵感来源于这篇文章【Vue】表格可拖拽滚动,作者已给出原理,感兴趣的可以去了解
实现效果
我用的的录制gif软件,鼠标没有变成抓取手势,实际是可以的
如果没有选中文字的需求,可以在mousemove监听中的 if 判断里加上 e.preventDefault();这样拖动时就会很流畅,不受到选中文字影响。
阻止选中文字效果
找到el-table元素对应的目标元素(VUE3+Element-Plus)
首先给el-table添加ref,并获取元素
<template> <el-table :data="tableData" ref="tableRef"></el-table> </template> <script lang="ts" setup> const tableRef = ref(null); console.log(tableRef); </script>
复制
我们要找的是包裹table的第一父元素,
.el-scrollbar__wrap类名盒子
就是我们要找的目标元素
这里解释一下为什么.el-scrollbar__wrap是父盒子:
我们可以看到.el-scrollbar__view盒子里包裹的是table标签,那么.el-scrollbar__view盒子的宽度就是整个表格的宽度,.el-scrollbar__wrap盒子的宽度又是其父元素的宽度,那么 .el-scrollbar__wrap就是.el-scrollbar__view的父盒子,.el-scrollbar__view子盒子宽度有溢出,.el-scrollbar__wrap父盒子则出现滚动条,使得table可以横向滚动
因此我们要找的目标元素就是
.el-scrollbar__wrap
<template> <el-table :data="tableData" ref="tableRef"></el-table> </template> <script lang="ts" setup> const tableRef = ref(null); //获取目标元素 console.log(tableRef.value.$refs.scrollBarRef.wrapRef); </script>
复制
实现拖拽横向滚动 (通过改变目标元素的scrollLeft值)
需要捕获鼠标事件及拖拽事件并更改目标元素的scrollLeft值则可实现横向滚动
涉及到的事件包括:mousedown,mouseup,mousemove, mouseleave, dragstart,给表格元素添加相应的事件
mousedown
鼠标按下事件,即鼠标按下但未释放的动作
1.可拖拽状态改为允许拖拽。2.记录鼠标位置。3.鼠标样式改为小手。
tableDataRef.value.$el.addEventListener('mousedown', (e: MouseEvent) => { // 拿到目标元素 tableBody = tableDataRef.value.$refs.scrollBarRef.wrapRef; // 拖拽状态改为允许拖拽/鼠标样式修改公共方法 setMouseFlag(true, tableBody); // 记录鼠标按下位置 mouseStart = e.clientX; // 记录元素当前scrollLeft值 startX = tableBody.scrollLeft; // 添加 dragstart 事件监听器 document.addEventListener('dragstart', handleDragStart); });
复制
mousemove
鼠标移动事件,即鼠标在元素内移动的动作
1.判断是否可拖拽。2.允许拖拽时记录鼠标移动距离。3.修改目标元素scrollLeft值。
tableDataRef.value.$el.addEventListener('mousemove', (e: MouseEvent) => { if (mouseFlag) { let offset = e.clientX - mouseStart; (tableBody as HTMLElement).scrollLeft = startX - offset; } });
复制
mouseup
鼠标释放事件,即鼠标按下后释放的动作
1.可拖拽状态改为禁止拖拽。2.鼠标样式恢复。
tableDataRef.value.$el.addEventListener('mouseup', () => { // console.log('鼠标左键松开++++++++++++'); setMouseFlag(false, tableBody); // 移除 dragstart 事件监听器 document.removeEventListener('dragstart', handleDragStart); });
复制
检测到禁止光标手势
监听拖动时,判断是否出现禁止小手标识
如果你选中文字然后点击拖拽时在浏览器中鼠标光标就会变成红色禁止符号
const handleDragStart = (e: DragEvent) => { // console.log('禁止光标手势出现'); // 取消默认的拖动效果 e.preventDefault(); setMouseFlag(false, tableBody); };
复制
拖拽状态改为允许拖拽/鼠标样式修改公共方法
const setMouseFlag = (flag: boolean, tableBody?: HTMLElement | null) => { mouseFlag = flag; if (tableBody) { tableBody.style.cursor = flag ? 'grab' : 'auto'; } };
复制
封装成统一函数dragTable,并将其定义为全局变量
/src/utils/common.ts
//设置el-table可进行鼠标左键按下左右拖动 interface TableDataRef { value: { $el: HTMLElement; $refs: { scrollBarRef: { wrapRef: HTMLElement; }; }; }; } export const dragTable = (tableDataRef: TableDataRef) => { let mouseFlag = false, mouseStart = 0, startX = 0, tableBody: HTMLElement | null = null; //鼠标按下事件,即鼠标按下但未释放的动作。 tableDataRef.value.$el.addEventListener('mousedown', (e: MouseEvent) => { tableBody = tableDataRef.value.$refs.scrollBarRef.wrapRef; setMouseFlag(true, tableBody); // mouseFlag = true; mouseStart = e.clientX; startX = tableBody.scrollLeft; // tableBody.style.cursor = 'grab'; // 添加 dragstart 事件监听器 document.addEventListener('dragstart', handleDragStart); }); //鼠标释放事件,即鼠标按下后释放的动作。 tableDataRef.value.$el.addEventListener('mouseup', () => { // console.log('鼠标左键松开++++++++++++'); /* mouseFlag = false; (tableBody as HTMLElement).style.cursor = 'auto'; */ setMouseFlag(false, tableBody); // 移除 dragstart 事件监听器 document.removeEventListener('dragstart', handleDragStart); }); //鼠标移动事件,即鼠标在元素内移动的动作。 tableDataRef.value.$el.addEventListener('mousemove', (e: MouseEvent) => { if (mouseFlag) { let offset = e.clientX - mouseStart; (tableBody as HTMLElement).scrollLeft = startX - offset; } }); //鼠标离开事件,即鼠标移动到元素外触发这个事件。 tableDataRef.value.$el.addEventListener('mouseleave', (e: MouseEvent) => { handleDragStart((e as any)) }); // 检测到禁止光标手势 const handleDragStart = (e: DragEvent) => { // console.log('禁止光标手势出现'); // 取消默认的拖动效果 e.preventDefault(); setMouseFlag(false, tableBody); }; const setMouseFlag = (flag: boolean, tableBody?: HTMLElement | null) => { mouseFlag = flag; if (tableBody) { tableBody.style.cursor = flag ? 'grab' : 'auto'; } }; };
复制
main.ts
import { dragTable } from '@/utils/common'; const app = createApp(App); // 全局方法挂载 app.config.globalProperties.$dragTable = dragTable;
复制
使用全局变量
<template> <el-table :data="tableData" ref="tableRef"></el-table> </template> <script lang="ts" setup> import { ComponentInternalInstance, ref } from 'vue'; const { proxy } = getCurrentInstance() as ComponentInternalInstance; const tableRef = ref(null); nextTick(() => { proxy?.$dragTable(tableRef) }) </script>
复制
补充(VUE2 + Element)
可参考这篇文章利用 bodyWrapper 实现表格拖动
el-table 中获取 bodyWrapper 的方法
注意:可能存在Element版本差异,建议下面的获取方式都试试
之前获取 bodyWrapper 方法是
this.$refs.myTable.bodyWrapper
复制
现在上面这种方法获取不到了,要更改为
this.$refs.myTable.$elTable.bodyWrapper 或 this.$refs.myTable.$refs.table.bodyWrapper
复制
console.log(this.$refs.mytable)
复制
总结
元素可滚动的前提条件是元素的宽度或高度超出给定区域,且开启了滚动条
拖拽需结合mousedown,mousemove,mouseup,mouseleave,dragstart事件实现
竖向滚动同理,修改ScrollTop值即可