【Vue】表格可拖拽滚动

【问题】表格横向太长,横向滚动条需滚动至表格最底部才能拖动,不便于浏览。
【需求】基于elment的el-table组件生成的表格,使其可以横向拖拽滚动。

    • 一、横向滚动原理
    • 二、找到el-table元素对应的目标元素
    • 三、通过改变目标元素的scrollLeft值实现横向滚动
    • 总结

一、横向滚动原理

要解决这个问题,首先需要知道横向滚动如何实现。
JS中滚动相关的属性包括:scrollWidth、scrollHeight、scrollLeft 、scrollTop

【条件】 元素可滚动的前提条件是元素的宽度或高度超出给定区域,且开启了滚动条

  • element.scrollWidth:返回元素的整体宽度,包括由于溢出而无法展示在网页的不可见部分。
  • element.scrollLeft :返回元素的整体高度,包括由于溢出而无法展示在网页的不可见部分。

scrollWidth示例如下:
【Vue】表格可拖拽滚动_第1张图片

  • element.scrollLeft:返回元素左边缘与视图之间的距离,这里的视图指的是元素的内容(包括子元素以及内容)。
  • element.scrollHeight :返回元素上边缘与视图之间的距离。

【拓展】JS中相关概念有很多,包括client系列,offset系列,scroll系列,详细解释可参考https://juejin.cn/post/7116306912198524959#heading-22

【注意】滚动时改变的是父元素的scrollLeft/scrollHeight。

以scrollLeft为例,初始值为0,此时子元素最左端与父元素最左端重合;滚动条右滑,子元素向左移动,父元素的scrollLeft为正值。
【Vue】表格可拖拽滚动_第2张图片
【例】 父元素宽度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结果为具体数值,且为正值。

【Vue】表格可拖拽滚动_第3张图片
【总结】 因此,横向滚动的关键在于找到溢出元素的父元素,并通过改变其scrollLeft值实现横向滚动。

二、找到el-table元素对应的目标元素

由于elment组件进行了很多包装,所以较难定位目标元素,我们可以先找到el-table元素,然后逐步寻找。
这里使用element-plus和vue3写一个简单的例子。tableData有6个属性,显然这里没显示完整,并出现了滚动条。
【Vue】表格可拖拽滚动_第4张图片
首先拿到这个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>

先打印一下这个元素看看,可以看到有很多属性。

【Vue】表格可拖拽滚动_第5张图片

我们的目的是拿到包裹表格的父元素,参考这篇文章利用 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>

【Vue】表格可拖拽滚动_第6张图片

发现并不起作用,打印tableBody,发现其scrollLeft没有改变,一直是0。说明我们找的元素不对,这个元素不是包裹table的第一父元素。
顺着这个思路,打印tableRef.value.$refs看看上面有哪些元素。

【Vue】表格可拖拽滚动_第7张图片
观察层级结构,发现这些元素对应的其实就是红框里面的元素。

【Vue】表格可拖拽滚动_第8张图片
然后我们发现.el-table__body其实是藏在.el-scrollbar下面的。
【Vue】表格可拖拽滚动_第9张图片

然后查看这些元素的样式,找到包裹table的第一层父元素。可以看到.el-scrollbar__view包裹的元素宽度是整个表格的宽度,.el-scrollbar__wrap包裹的元素宽度是父元素的宽度。
.el-scrollbar__view就是子元素,宽度有溢出,.el-scrollbar__wrap就是父元素,可以横向滚动。
因此我们要找的目标元素就是 .el-scrollbar__wrap包裹的元素

【Vue】表格可拖拽滚动_第10张图片

【Vue】表格可拖拽滚动_第11张图片

对应的获取代码为:tableDataRef.value.$refs.scrollBarRef.wrapRef

【Vue】表格可拖拽滚动_第12张图片

回到我们的测试demo,打印这个元素的ScrollLeft。点击按钮,发现ScrollLeft值已经发生改变,表格也发生了移动。

const tableRef = ref(null);
const handleClick = () => {
    const tableBody = tableRef.value.$refs.scrollBarRef.wrapRef;
    tableBody.scrollLeft += 20;
    console.log(tableBody.scrollLeft);
};

【Vue】表格可拖拽滚动_第13张图片

【总结】 要找的目标元素必须是包裹table的第一父元素.el-scrollbar__wrap包裹的元素就是我们要找的目标元素。

三、通过改变目标元素的scrollLeft值实现横向滚动

那么接下来就简单了,只需要捕获鼠标事件并给目标元素的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;
    }
  });
};

用手机简单录制了一个实现效果,有点模糊,可以凑合看看哈哈

总结

  • 元素可滚动的前提条件是元素的宽度或高度超出给定区域,且开启了滚动条
  • 需要找到直接包裹溢出元素的第一父元素且该父元素开启了滚动条,才能修改ScrollLeft值,否则修改无效。
  • 拖拽需结合mousedown,mousemove,mouseup事件实现。
  • vue2+elementui同理,按照同样的思路找到目标元素即可。
  • 竖向滚动同理,修改ScrollTop值即可。

你可能感兴趣的:(Vue,vue.js,javascript,前端,elementui)