【Vue】表格可拖拽滚动

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

一、横向滚动原理

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

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

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

scrollWidth示例如下:
scrollWidth示例

  • 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">sonsonsonsonsonsonsonsonson</div>
    </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值实现横向滚动。

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

由于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看看上面有哪些元素。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4775575edc2a47479bbc28c630
观察层级结构,发现这些元素对应的其实就是红框里面的元素。

在这里插入图片描述
然后我们发现.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值实现横向滚动

那么接下来就简单了,只需要捕获鼠标事件并给目标元素的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值即可。
  • 20
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个使用Vue.js编写的带滚动条的表格示例: HTML: ```html <div id="app"> <div class="table-container"> <table class="table"> <thead> <tr> <th>序号</th> <th>姓名</th> <th>年龄</th> <th>性别</th> </tr> </thead> <tbody> <tr v-for="(item, index) in tableData" :key="index"> <td>{{index + 1}}</td> <td>{{item.name}}</td> <td>{{item.age}}</td> <td>{{item.gender}}</td> </tr> </tbody> </table> <div class="scrollbar"> <div class="thumb" :style="{height: thumbHeight+'px', top: thumbTop+'px'}" @mousedown="startDrag"></div> </div> </div> </div> ``` CSS: ```css .table-container { position: relative; } .table { width: 100%; border-collapse: collapse; } .table th, .table td { border: 1px solid #ccc; padding: 8px; text-align: center; } .scrollbar { position: absolute; right: 0; top: 0; bottom: 0; width: 10px; overflow: hidden; background-color: #f5f5f5; } .thumb { position: absolute; left: 0; width: 100%; background-color: #ccc; cursor: pointer; } ``` JavaScript: ```javascript new Vue({ el: '#app', data: { tableData: [ { name: '张三', age: 18, gender: '男' }, { name: '李四', age: 22, gender: '女' }, { name: '王五', age: 30, gender: '男' }, { name: '赵六', age: 25, gender: '女' }, { name: '钱七', age: 28, gender: '男' }, { name: '孙八', age: 20, gender: '女' }, { name: '周九', age: 24, gender: '男' }, { name: '吴十', age: 26, gender: '女' } ], thumbTop: 0, thumbHeight: 50, dragStart: false, startY: 0 }, computed: { tableHeight() { return this.$el.querySelector('.table-container').offsetHeight; }, contentHeight() { return this.$el.querySelector('tbody').offsetHeight; }, scrollHeight() { return this.tableHeight - this.contentHeight; } }, methods: { startDrag(e) { this.dragStart = true; this.startY = e.clientY; }, onDrag(e) { if (this.dragStart) { const deltaY = e.clientY - this.startY; const newTop = this.thumbTop + deltaY; if (newTop >= 0 && newTop <= this.scrollHeight) { this.thumbTop = newTop; const scrollPercent = this.thumbTop / this.scrollHeight; this.$el.querySelector('tbody').style.transform = `translateY(-${scrollPercent * 100}%)`; } this.startY = e.clientY; } }, stopDrag() { this.dragStart = false; } }, mounted() { this.thumbHeight = this.tableHeight / this.contentHeight * this.tableHeight; this.$el.querySelector('.scrollbar').addEventListener('mousemove', this.onDrag); this.$el.querySelector('.scrollbar').addEventListener('mouseup', this.stopDrag); }, beforeDestroy() { this.$el.querySelector('.scrollbar').removeEventListener('mousemove', this.onDrag); this.$el.querySelector('.scrollbar').removeEventListener('mouseup', this.stopDrag); } }) ``` 这个示例中,我们使用了CSS中的`position: absolute`将滚动条和表格分开,并使用计算属性和数据绑定来实现滚动条的拖动表格内容的滚动。我们为滚动条的拖动区域绑定了`mousedown`事件,在鼠标按下时将`dragStart`设置为`true`,表示开始拖动。在`mousemove`事件中,如果`dragStart`为`true`,则计算拖动的距离,更新滚动条的位置和表格内容的偏移量。在`mouseup`事件中,将`dragStart`设置为`false`,表示停止拖动。最后,我们在组件的`mounted`和`beforeDestroy`钩子中分别添加和移除事件监听器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值