需求背景
由于项目表格的列数很多,所以table
必然会出现横向滚动条。然而如果页面本身内容较多,出现纵向的滚动条,就会出现需要先滚动到表格底部(table
的横向滚动条默认在容器底部),才能进行左右的拉动,这给部分用户带来了不便。
常规方案
- 由于列表页一般下方没有其他内容,所以可以通过动态设置
table
的height
来规避掉纵向滚动条的出现,从而保证横向滚动条始终位于视口底部。缺点就是在一些小屏幕,例如笔记本上,会导致表格可展现的条数过少。 - 告知用户使用键盘操作,
el-table
的可访问性很优秀,可以通过← →
快捷键来横向滚动。或者按住鼠标中键滑动也可以。缺点就是不少用户不知道或者不习惯这样的操作方式。个人认为也可以做引导,但要考虑设计友好的交互和用户的接受程度。(ps: 手动狗头.jpg)
效果的实现
先看一下最终效果,如下图所示:
一句话概括就是当表格容器底部不在可视区域范围内时,横向滚动条固定在视口底部的位置。
思路一
自己实现一个横向滚动条固定在视口底部,同步这个滚动条和el-table
的scroll
事件。当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
的时候初始化自定义滚动条,采用的是PerfectScrollbar
。PerfectScrollbar
实际生成的横向滚动条<div class='ps__rail-x'></div>
,通过动态设置滚动条的top
值来维持滚动条在视口底部。
一个简单的几何公式:
//如果table内部还有滚动条的话需要加上_tbody.scrollTop
//视口的高度 - tbody基于视口的top值 - 横向滚动条容器的高度
const _top = window.innerHeight - _tbody.getBoundingClientRect().top - railX.clientHeight;
在el-table
组件更新后重新初始化滚动条,scroll
和resize
事件触发时更新滚动条位置即可。
如果表格有固定列,由于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内也有滚动,所以就没去深究解决方案。
如果真的出现双重滚动条的情况,可以考虑使用上文中的思路一去实现。
参考
项目地址
今天的文章el-table横向滚动条始终位于可视区域的实现分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18782.html