el-table横向滚动条始终位于可视区域的实现

el-table横向滚动条始终位于可视区域的实现由于项目表格的列数很多,所以table必然会出现横向滚动条。然而如果页面本身内容较多,出现纵向的滚动条,就会出现需要先滚动到表格底部(table的横向滚动条默认在容器底部),才能进行左右的拉动,这给部分用户带来了不便。 由于列表页一般下方没有其他内容,所以可以通过动态设置tab…

需求背景

由于项目表格的列数很多,所以table必然会出现横向滚动条。然而如果页面本身内容较多,出现纵向的滚动条,就会出现需要先滚动到表格底部(table的横向滚动条默认在容器底部),才能进行左右的拉动,这给部分用户带来了不便。

常规方案

  1. 由于列表页一般下方没有其他内容,所以可以通过动态设置tableheight来规避掉纵向滚动条的出现,从而保证横向滚动条始终位于视口底部。缺点就是在一些小屏幕,例如笔记本上,会导致表格可展现的条数过少。
  2. 告知用户使用键盘操作,el-table的可访问性很优秀,可以通过← →快捷键来横向滚动。或者按住鼠标中键滑动也可以。缺点就是不少用户不知道或者不习惯这样的操作方式。个人认为也可以做引导,但要考虑设计友好的交互和用户的接受程度。(ps: 手动狗头.jpg)

效果的实现

先看一下最终效果,如下图所示:

自定义横向滚动条示意图

一句话概括就是当表格容器底部不在可视区域范围内时,横向滚动条固定在视口底部的位置。

思路一

自己实现一个横向滚动条固定在视口底部,同步这个滚动条和el-tablescroll事件。当el-table原生的横向滚动条出现在可视区域范围内时,隐藏自定义滚动条。

思路二

el-table内部生成一个自定义横向滚动条,当el-table原生的横向滚动条没出现在可视区域范围内时,将自定义滚动条调整到视口底部位置,反之隐藏该自定义滚动条。

我采用了思路二。由于用的是Vue,又涉及到操作Dom,故而把这个功能封装成了一个指令的形式,使用起来在el-table上添加指令即可。

核心代码如下:

//自定义滚动条
import PerfectScrollbar from 'perfect-scrollbar';
//对应的css
import "perfect-scrollbar/css/perfect-scrollbar.css";


const updateScrollBar = (el) => {
    const railX = el.querySelector(".ps__rail-x");
    const _tbody = el;
    //如果table内部还有滚动条的话需要加上_tbody.scrollTop
    const _top = window.innerHeight - _tbody.getBoundingClientRect().top  - railX.clientHeight;
    railX.style.top = `${_top}px`;
    railX.style.opacity = "1";
    railX.style.display = "block";
}

const el_scrollBar = (el) => {
    if (el._ps_ instanceof PerfectScrollbar) {
        el._ps_.update();
    } else {
        el._ps_ = new PerfectScrollbar(el, {
            suppressScrollX: false,
            suppressScrollY: true //y方向禁止
        });
        // setTimeout(() => {
        //     el._ps_.update();
        // }, 17);
    }
};

let isScrolling = false;
let _scrollHander = null;
let _resizeHander = null;

const directive = {
    inserted(el) {
        el = el.querySelector(".el-table__body-wrapper");
        if (!el) {
            return console.warn("未发现className为el-table__body-wrapper的dom");
        }
        const rules = ["fixed", "absolute", "relative"];
        if (!rules.includes(window.getComputedStyle(el, null).position)) {
            console.error(`perfect-scrollbar所在的容器的position属性必须是以下之一:${rules.join("、")}`)
        }
        el_scrollBar(el);
        updateScrollBar(el);

        //注册scroll和resize事件
        _scrollHander = () => {
            if (!isScrolling) {
                window.requestAnimationFrame(() => {
                    updateScrollBar(el);
                    isScrolling = false;
                });
            }
            isScrolling = true;
        };
        
        _resizeHander = () => {
            updateScrollBar(el)
        }

        document.addEventListener("scroll", _scrollHander)
        window.addEventListener("resize", _resizeHander)
    },
    componentUpdated(el, binding, vnode) {
        const {
            expression
        } = binding;

        el = el.querySelector(".el-table__body-wrapper");
        if (!el) {
            return console.warn("未发现className为el-table__body-wrapper的dom");
        }

        const handler = () => vnode.context[expression].apply();

        vnode.context.$nextTick(
            () => {
                try {
                    el_scrollBar(el);
                    updateScrollBar(el);
                    if (expression) {
                        handler()
                    }
                } catch (error) {
                    console.error(error);
                }
            }
        )
    },
    unbind() {
        document.removeEventListener("scroll", _scrollHander)
        window.removeEventListener("resize", _resizeHander)
    }
}

export default directive;

inserted的时候初始化自定义滚动条,采用的是PerfectScrollbarPerfectScrollbar实际生成的横向滚动条<div class='ps__rail-x'></div>,通过动态设置滚动条的top值来维持滚动条在视口底部。

一个简单的几何公式:

//如果table内部还有滚动条的话需要加上_tbody.scrollTop
//视口的高度 - tbody基于视口的top值 - 横向滚动条容器的高度
const _top = window.innerHeight - _tbody.getBoundingClientRect().top  - railX.clientHeight;

el-table组件更新后重新初始化滚动条,scrollresize事件触发时更新滚动条位置即可。

如果表格有固定列,由于el-table的固定列设置了z-index:4,所以需要给滚动条容器设置大一点的z-index

.ps__rail-x {
  display: block;
  z-index: 99; /*大于fixed table 的z-index*/
}

在表格本身的横向滚动条出现在可视区域内后,由于自定义滚动条的位置已经处于表格下方,一部分在原生滚动条所在区域,原生滚动条会覆盖自定义滚动条;一部分溢出在表格外部(el-table设置了overflow:hidden);两部分都不可见,因此未对自定义滚动条做隐藏。

如果需要隐藏滚动条,可以监听表格内最后一个tr的可见性。

局限性

在实现的过程中发现,PerfectScrollbar的纵向滚动条会影响带有固定列的el-table的滚动同步(固定列是基于多个table实现的),就把y方向上的滚动禁止掉了。但这样会导致el-table纵向滚动失效。

考虑到一般不会有双重滚动条,即body内有滚动,table内也有滚动,所以就没去深究解决方案。

如果真的出现双重滚动条的情况,可以考虑使用上文中的思路一去实现。

参考

vue下使用perfect-scrollbar

vue官网#自定义指令

项目地址

github.com/juenanfeng/…

今天的文章el-table横向滚动条始终位于可视区域的实现分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18782.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注