vue中el-table实现自动吸顶效果(支持fixed)

目录

  • 前言
  • 实现思路
  • 效果:
  • 使用:
  • 主要源码:

前言
看了很多案例,从简单的角度,position:sticky,似乎是比较理想的选择,可是当el-table设置了fixed后,这里的fixed会失效。最后还是采用了js监听滚动的思路实现。

实现思路
  • 表格距离顶部的距离
  • 设置表格距离顶部多少就吸顶—offsetTop1
  • 获取滚动条滚动的距离
  • 当滚动条滚动 offsetTop1 后,表格就自动吸顶

效果:

使用:
在el-table标签中配置:v-sticky="{ top: 0, parent:'#appMainDom'}",
...

说明


参数名字 类型 说明
top Number 滚动条距离顶部多少像素,自动吸顶
parent String 滚动的dom元素,内部使用querySelector获取该元素

gitee案例源码:
https://gitee.com/kaiking_g/test-element-by-vue.git

主要源码:
/** * 思路: * 表格距离顶部的距离 * 设置表格距离顶部多少就吸顶---offsetTop1 * 获取滚动条滚动的距离 * 当滚动条滚动 offsetTop1 后,表格就自动吸顶 */import Vue from 'vue'const tableStickyObj = {}const __STICKY_TABLE = {// 给固定头设置样式doFix (dom, top, data) {const { uid, domType, isExist } = dataconst uObj = tableStickyObj[uid]const curObj = uObj[domType]const headerRect = tableStickyObj[uid].headerRectif (!isExist) {dom.style.position = 'fixed'dom.style.zIndex = '2001'dom.style.top = top + 'px'}uObj.tableWrapDom.style.marginTop = headerRect.height + 'px'if (domType === 'fixed') {dom.style.left = curObj.left + 'px'} else if (domType === 'fixedRight') {dom.style.left = curObj.left + 1 + 'px'}},// 给固定头取消样式removeFix (dom, data) {const { uid, domType } = data// dom.parentNode.style.paddingTop = 0const uObj = tableStickyObj[uid]const curObj = uObj[domType]dom.style.position = 'static'dom.style.top = '0'dom.style.zIndex = '0'uObj.tableWrapDom.style.marginTop = '0'if (domType === 'fixed') {curObj.dom.style.top = '0'} else if (domType === 'fixedRight') {curObj.dom.style.top = '0'}},// 给固定头添加classaddClass (dom, fixtop, data) {fixtop = fixtop || 0const isExist = dom.classList.contains('fixed')data.isExist = !!isExistif (!isExist) { // 若有,就不再添加dom.classList.add('fixed')}this.doFix(dom, fixtop, data)},// 给固定头移除classremoveClass (dom, data) {if (dom.classList.contains('fixed')) {dom.classList.remove('fixed')this.removeFix(dom, data)}},/*** 计算某元素距离相对父元素的top距离* @param {Nodes} e 某元素* @param {String} domId 父元素id* @param {Boolean} isParent 是否父元素* @returns {Number}*/getPosY (el, domId) {let offset = 0const pDom = el.offsetParentif (pDom != null && '#' + el.id !== domId) {offset = el.offsetTopoffset += this.getPosY(pDom, domId)}return offset},// 获取元素的横坐标(相对于窗口)getPosX (e) {var offset = e.offsetLeftif (e.offsetParent != null) offset += this.getPosX(e.offsetParent)return offset},fixHead (scrollDom, el, uid, binding) {this.fixHead1(this, { scrollDom, el, uid, binding })},// 具体判断是否固定头的主函数fixHead1: sticky_throttle((_this, { scrollDom, el, uid, binding }) => {const top = binding.value.top/*** myTop 当前元素距离滚动父容器的高度,* fixtop 当前元素需要设置的绝对定位的高度* parentHeight 滚动父容器的高度*/// 表头DOM节点const headerWrapDom = el.children[1] // el-table__header-wrapperconst headerTop = tableStickyObj[uid].headerRect.topconst scrollTop = scrollDom.scrollTopconst fixedHeadDom = tableStickyObj[uid].fixed.headerDomconst fixedHeadRightDom = tableStickyObj[uid].fixedRight.headerDomif (scrollTop >= headerTop) {const fixtop = top + scrollDom.getBoundingClientRect().top// 如果表头滚动到 父容器顶部了。fixed定位_this.addClass(headerWrapDom, fixtop, { domType: 'mainBody', uid })fixedHeadDom && _this.addClass(fixedHeadDom, fixtop, { domType: 'fixed', uid })fixedHeadRightDom && _this.addClass(fixedHeadRightDom, fixtop, { domType: 'fixedRight', uid })} else {// 如果表格向上滚动 又滚动到父容器里。取消fixed定位_this.removeClass(headerWrapDom, { domType: 'mainBody', uid })fixedHeadDom && _this.removeClass(fixedHeadDom, { domType: 'fixed', uid })fixedHeadRightDom && _this.removeClass(fixedHeadRightDom, { domType: 'fixedRight', uid })}}, 100, { eventType: 'fixHead111' }),//setHeadWidth (data) {this.setHeadWidth1(this, data)},// 设置头部固定时表头外容器的宽度写死为表格body的宽度setHeadWidth1: sticky_debounce((_this, data) => {const { el, uid, binding, eventType } = dataconst { scrollDom } = tableStickyObj[uid]const headerWrapDom = el.children[1] // el-table__header-wrapperconst headerH = headerWrapDom.offsetHeightconst distTop = _this.getPosY(headerWrapDom, binding.value.parent)const scrollDistTop = _this.getPosY(scrollDom) // 滚动条距离顶部的距离tableStickyObj[uid].headerRect.top = distTop + headerH - scrollDistTop / 3 // 表头距离顶部的距离 - 表头自身高度 - 滚动条距离顶部的距离tableStickyObj[uid].headerRect.height = headerH// tableStickyObj[uid].headerRect.width = tableW// debugger// fixed left/right header// 确保每次刷新,只获取一次// tableStickyObj[uid].fixed.dom = ''_this.initFixedWrap({ el, uid, eventType, key: 'fixed', className: 'el-table__fixed', className1: 'el-table__fixed-header-wrapper' })_this.initFixedWrap({ el, uid, eventType, key: 'fixedRight', className: 'el-table__fixed-right', className1: 'el-table__fixed-header-wrapper' })// debugger// 获取到当前表格个表格body的宽度const bodyWrapperDom = el.getElementsByClassName('el-table__body-wrapper')[0]const width = getComputedStyle(bodyWrapperDom).width// 给表格设置宽度。这里默认一个页面中的多个表格宽度是一样的。所以直接遍历赋值,也可以根据自己需求,单独设置const tableParent = el.getElementsByClassName('el-table__header-wrapper')for (let i = 0; i < tableParent.length; i++) {tableParent[i].style.width = width}// debugger_this.fixHead(scrollDom, el, uid, binding) // 判断顶部是否已吸顶的一个过程}),initFixedWrap (data) {const { key, el, eventType, className, className1, uid } = data// 确保每次刷新,只获取一次if (eventType === 'resize' || !tableStickyObj[uid][key].dom) {const tableFixedDom = el.getElementsByClassName(className)if (tableFixedDom.length) {const fixedDom = tableFixedDom[0]const arr = fixedDom.getElementsByClassName(className1) //const headW = getComputedStyle(fixedDom).widthtableStickyObj[uid][key].dom = fixedDomif (arr.length) {const distLeft = this.getPosX(fixedDom) // 距离窗口左侧的距离const headDom = arr[0]headDom.style.width = headWtableStickyObj[uid][key].left = distLeft // 距离窗口左边像素if (key === 'fixedRight') { // right-fixed 的特别之处headDom.classList.add('scroll-bar-h0')headDom.style.overflow = 'auto'headDom.scrollLeft = headDom.scrollWidthheadDom.style.overflow = 'hidden' // 设置了滚动到最后,设置不可滚动} else {headDom.style.overflow = 'hidden'}tableStickyObj[uid][key].headerDom = headDom // 取第一个}}}},// 监听父级的某些变量(父级一定要有才能被监听到)watched ({ el, binding, vnode, uid }) {// 监听左侧导航栏是否折叠vnode.context.$watch('isNavFold', (val) => {vnode.context.$nextTick(() => {setTimeout(() => {// debuggerthis.setHeadWidth({ el, uid, binding, eventType: 'resize' })}, 200)})})}}/** * 节流函数: 指定时间间隔内只会执行一次任务 * @param {function} fn * @param {Number} interval */function sticky_throttle (fn, interval = 300) {let canRun = truereturn function () {if (!canRun) returncanRun = falsesetTimeout(() => {fn.apply(this, arguments)canRun = true}, interval)}}/** * 防抖: 指定时间间隔内只会执行一次任务,并且该时间段内再触发,都会重新计算时间。(函数防抖的非立即执行版) * 在频繁触发某些事件,导致大量的计算或者非常消耗资源的操作的时候,防抖可以强制在一段连续的时间内只执行一次 * */function sticky_debounce (fn, delay, config) {const _delay = delay || 200config = config || {}// const _this = this // 该this指向common.jsreturn function () {const th = this // 该this指向实例const args = arguments// debounceNum++// let str = `, label: ${th && th.listItem && th.listItem.label}`if (fn.timer) {clearTimeout(fn.timer)fn.timer = null} else {// fn.debounceNum = debounceNum}fn.timer = setTimeout(function () {// str = `, label: ${th && th.listItem && th.listItem.label}`fn.timer = nullfn.apply(th, args)}, _delay)}}// 全局注册 自定义事件Vue.directive('sticky', {// 当被绑定的元素插入到 DOM 中时……inserted (el, binding, vnode) {// 获取当前vueComponent的ID。作为存放各种监听事件的keyconst uid = vnode.componentInstance._uid// 获取当前滚动的容器是什么。如果是document滚动。则可默认不传入parent参数const scrollDom = document.querySelector(binding.value.parent) || document.body // TODO:得考虑没有 binding.value.parent 的情况,重新登录直接进到内页会出现if (!tableStickyObj[uid]) {tableStickyObj[uid] = {uid,fixFunObj: {}, // 用于存放滚动容器的监听scroll事件setWidthFunObj: {}, // 用于存放页面resize后重新计算head宽度事件autoMoveFunObj: {}, // 用户存放如果是DOM元素内局部滚动时,document滚动时,fix布局的表头也需要跟着document一起向上滚动scrollDomRect: {},headerRect: { top: 0, left: 0 },fixed: {}, // 表格左浮动fixedRight: {}, // 表格右浮动// binding,// el,tableWrapDom: el.getElementsByClassName('el-table__body-wrapper')[0],scrollDom}}__STICKY_TABLE.watched({ el, binding, vnode, uid }) // 监听父级的某些变量// 当window resize时 重新计算设置表头宽度,并将监听函数存入 监听函数对象中,方便移除监听事件window.addEventListener('resize', (tableStickyObj[uid].setWidthFunObj = () => {__STICKY_TABLE.setHeadWidth({ el, uid, binding, eventType: 'resize' }) // 首先设置表头宽度}))// 给滚动容器加scroll监听事件。并将监听函数存入 监听函数对象中,方便移除监听事件scrollDom.addEventListener('scroll', (tableStickyObj[uid].fixFunObj = (e) => {__STICKY_TABLE.fixHead(scrollDom, el, uid, binding)}))},// component 更新后。重新计算表头宽度componentUpdated (el, binding, vnode) {const uid = vnode.componentInstance._uid__STICKY_TABLE.setHeadWidth({ el, uid, binding, eventType: 'componentUpdated' })},// 节点取消绑定时 移除各项监听事件。unbind (el, binding, vnode) {const uid = vnode.componentInstance._uidwindow.removeEventListener('resize', tableStickyObj[uid].setWidthFunObj)const scrollDom = document.querySelector(binding.value.parent) || documentscrollDom.removeEventListener('scroll', tableStickyObj[uid].fixFunObj)if (binding.value.parent) {document.removeEventListener('scroll', tableStickyObj[uid].autoMoveFunObj)}}})

【vue中el-table实现自动吸顶效果(支持fixed)】到此这篇关于vue中el-table实现自动吸顶效果(支持fixed)的文章就介绍到这了,更多相关el-table 自动吸顶内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读