el-table橫向滾動條始終位於可視區域的實現

需求背景

因爲項目表格的列數不少,因此table必然會出現橫向滾動條。然而若是頁面自己內容較多,出現縱向的滾動條,就會出現須要先滾動到表格底部(table的橫向滾動條默認在容器底部),才能進行左右的拉動,這給部分用戶帶來了不便。css

常規方案

  1. 因爲列表頁通常下方沒有其餘內容,因此能夠經過動態設置tableheight來規避掉縱向滾動條的出現,從而保證橫向滾動條始終位於視口底部。缺點就是在一些小屏幕,例如筆記本上,會致使表格可展示的條數過少。
  2. 告知用戶使用鍵盤操做,el-table的可訪問性很優秀,能夠經過← →快捷鍵來橫向滾動。或者按住鼠標中鍵滑動也能夠。缺點就是很多用戶不知道或者不習慣這樣的操做方式。我的認爲也能夠作引導,但要考慮設計友好的交互和用戶的接受程度。(ps: 手動狗頭.jpg)

效果的實現

先看一下最終效果,以下圖所示:html

自定義橫向滾動條示意圖

一句話歸納就是當表格容器底部不在可視區域範圍內時,橫向滾動條固定在視口底部的位置。vue

思路一

本身實現一個橫向滾動條固定在視口底部,同步這個滾動條和el-tablescroll事件。當el-table原生的橫向滾動條出如今可視區域範圍內時,隱藏自定義滾動條。node

思路二

el-table內部生成一個自定義橫向滾動條,當el-table原生的橫向滾動條沒出如今可視區域範圍內時,將自定義滾動條調整到視口底部位置,反之隱藏該自定義滾動條。git

我採用了思路二。因爲用的是Vue,又涉及到操做Dom,故而把這個功能封裝成了一個指令的形式,使用起來在el-table上添加指令便可。github

核心代碼以下:express

//自定義滾動條
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值來維持滾動條在視口底部。segmentfault

一個簡單的幾何公式:bash

//若是table內部還有滾動條的話須要加上_tbody.scrollTop
//視口的高度 - tbody基於視口的top值 - 橫向滾動條容器的高度
const _top = window.innerHeight - _tbody.getBoundingClientRect().top  - railX.clientHeight;
複製代碼

el-table組件更新後從新初始化滾動條,scrollresize事件觸發時更新滾動條位置便可。app

若是表格有固定列,因爲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/…

相關文章
相關標籤/搜索