vue中el-table實(shí)現(xiàn)自動(dòng)吸頂效果(支持fixed)
前言
看了很多案例,從簡(jiǎn)單的角度,position:sticky,似乎是比較理想的選擇,可是當(dāng)el-table設(shè)置了fixed后,這里的fixed會(huì)失效。最后還是采用了js監(jiān)聽滾動(dòng)的思路實(shí)現(xiàn)。
實(shí)現(xiàn)思路
- 表格距離頂部的距離
- 設(shè)置表格距離頂部多少就吸頂—offsetTop1
- 獲取滾動(dòng)條滾動(dòng)的距離
- 當(dāng)滾動(dòng)條滾動(dòng) offsetTop1 后,表格就自動(dòng)吸頂
效果:
使用:
在el-table標(biāo)簽中配置:v-sticky="{ top: 0, parent:'#appMainDom'}",
<el-table :data="tableData" style="margin:10px 0;width: 100%;" bordermax-height="800" class="sticky-head" v-sticky="{ top: 0, parent:'#appMainDom' }" > ... </el-table>
說明
參數(shù)名字 | 類型 | 說明 |
---|---|---|
top | Number | 滾動(dòng)條距離頂部多少像素,自動(dòng)吸頂 |
parent | String | 滾動(dòng)的dom元素,內(nèi)部使用querySelector獲取該元素 |
gitee案例源碼:
https://gitee.com/kaiking_g/test-element-by-vue.git
主要源碼:
/** * 思路: * 表格距離頂部的距離 * 設(shè)置表格距離頂部多少就吸頂---offsetTop1 * 獲取滾動(dòng)條滾動(dòng)的距離 * 當(dāng)滾動(dòng)條滾動(dòng) offsetTop1 后,表格就自動(dòng)吸頂 */ import Vue from 'vue' const tableStickyObj = {} const __STICKY_TABLE = { // 給固定頭設(shè)置樣式 doFix (dom, top, data) { const { uid, domType, isExist } = data const uObj = tableStickyObj[uid] const curObj = uObj[domType] const headerRect = tableStickyObj[uid].headerRect if (!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 = 0 const 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' } }, // 給固定頭添加class addClass (dom, fixtop, data) { fixtop = fixtop || 0 const isExist = dom.classList.contains('fixed') data.isExist = !!isExist if (!isExist) { // 若有,就不再添加 dom.classList.add('fixed') } this.doFix(dom, fixtop, data) }, // 給固定頭移除class removeClass (dom, data) { if (dom.classList.contains('fixed')) { dom.classList.remove('fixed') this.removeFix(dom, data) } }, /** * 計(jì)算某元素距離相對(duì)父元素的top距離 * @param {Nodes} e 某元素 * @param {String} domId 父元素id * @param {Boolean} isParent 是否父元素 * @returns {Number} */ getPosY (el, domId) { let offset = 0 const pDom = el.offsetParent if (pDom != null && '#' + el.id !== domId) { offset = el.offsetTop offset += this.getPosY(pDom, domId) } return offset }, // 獲取元素的橫坐標(biāo)(相對(duì)于窗口) getPosX (e) { var offset = e.offsetLeft if (e.offsetParent != null) offset += this.getPosX(e.offsetParent) return offset }, fixHead (scrollDom, el, uid, binding) { this.fixHead1(this, { scrollDom, el, uid, binding }) }, // 具體判斷是否固定頭的主函數(shù) fixHead1: sticky_throttle((_this, { scrollDom, el, uid, binding }) => { const top = binding.value.top /** * myTop 當(dāng)前元素距離滾動(dòng)父容器的高度, * fixtop 當(dāng)前元素需要設(shè)置的絕對(duì)定位的高度 * parentHeight 滾動(dòng)父容器的高度 */ // 表頭DOM節(jié)點(diǎn) const headerWrapDom = el.children[1] // el-table__header-wrapper const headerTop = tableStickyObj[uid].headerRect.top const scrollTop = scrollDom.scrollTop const fixedHeadDom = tableStickyObj[uid].fixed.headerDom const fixedHeadRightDom = tableStickyObj[uid].fixedRight.headerDom if (scrollTop >= headerTop) { const fixtop = top + scrollDom.getBoundingClientRect().top // 如果表頭滾動(dòng)到 父容器頂部了。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 { // 如果表格向上滾動(dòng) 又滾動(dòng)到父容器里。取消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) }, // 設(shè)置頭部固定時(shí)表頭外容器的寬度寫死為表格body的寬度 setHeadWidth1: sticky_debounce((_this, data) => { const { el, uid, binding, eventType } = data const { scrollDom } = tableStickyObj[uid] const headerWrapDom = el.children[1] // el-table__header-wrapper const headerH = headerWrapDom.offsetHeight const distTop = _this.getPosY(headerWrapDom, binding.value.parent) const scrollDistTop = _this.getPosY(scrollDom) // 滾動(dòng)條距離頂部的距離 tableStickyObj[uid].headerRect.top = distTop + headerH - scrollDistTop / 3 // 表頭距離頂部的距離 - 表頭自身高度 - 滾動(dòng)條距離頂部的距離 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 // 獲取到當(dāng)前表格個(gè)表格body的寬度 const bodyWrapperDom = el.getElementsByClassName('el-table__body-wrapper')[0] const width = getComputedStyle(bodyWrapperDom).width // 給表格設(shè)置寬度。這里默認(rèn)一個(gè)頁面中的多個(gè)表格寬度是一樣的。所以直接遍歷賦值,也可以根據(jù)自己需求,單獨(dú)設(shè)置 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) // 判斷頂部是否已吸頂?shù)囊粋€(gè)過程 }), 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).width tableStickyObj[uid][key].dom = fixedDom if (arr.length) { const distLeft = this.getPosX(fixedDom) // 距離窗口左側(cè)的距離 const headDom = arr[0] headDom.style.width = headW tableStickyObj[uid][key].left = distLeft // 距離窗口左邊像素 if (key === 'fixedRight') { // right-fixed 的特別之處 headDom.classList.add('scroll-bar-h0') headDom.style.overflow = 'auto' headDom.scrollLeft = headDom.scrollWidth headDom.style.overflow = 'hidden' // 設(shè)置了滾動(dòng)到最后,設(shè)置不可滾動(dòng) } else { headDom.style.overflow = 'hidden' } tableStickyObj[uid][key].headerDom = headDom // 取第一個(gè) } } } }, // 監(jiān)聽父級(jí)的某些變量(父級(jí)一定要有才能被監(jiān)聽到) watched ({ el, binding, vnode, uid }) { // 監(jiān)聽左側(cè)導(dǎo)航欄是否折疊 vnode.context.$watch('isNavFold', (val) => { vnode.context.$nextTick(() => { setTimeout(() => { // debugger this.setHeadWidth({ el, uid, binding, eventType: 'resize' }) }, 200) }) }) } } /** * 節(jié)流函數(shù): 指定時(shí)間間隔內(nèi)只會(huì)執(zhí)行一次任務(wù) * @param {function} fn * @param {Number} interval */ function sticky_throttle (fn, interval = 300) { let canRun = true return function () { if (!canRun) return canRun = false setTimeout(() => { fn.apply(this, arguments) canRun = true }, interval) } } /** * 防抖: 指定時(shí)間間隔內(nèi)只會(huì)執(zhí)行一次任務(wù),并且該時(shí)間段內(nèi)再觸發(fā),都會(huì)重新計(jì)算時(shí)間。(函數(shù)防抖的非立即執(zhí)行版) * 在頻繁觸發(fā)某些事件,導(dǎo)致大量的計(jì)算或者非常消耗資源的操作的時(shí)候,防抖可以強(qiáng)制在一段連續(xù)的時(shí)間內(nèi)只執(zhí)行一次 * */ function sticky_debounce (fn, delay, config) { const _delay = delay || 200 config = config || {} // const _this = this // 該this指向common.js return function () { const th = this // 該this指向?qū)嵗? 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 = null fn.apply(th, args) }, _delay) } } // 全局注冊(cè) 自定義事件 Vue.directive('sticky', { // 當(dāng)被綁定的元素插入到 DOM 中時(shí)…… inserted (el, binding, vnode) { // 獲取當(dāng)前vueComponent的ID。作為存放各種監(jiān)聽事件的key const uid = vnode.componentInstance._uid // 獲取當(dāng)前滾動(dòng)的容器是什么。如果是document滾動(dòng)。則可默認(rèn)不傳入parent參數(shù) const scrollDom = document.querySelector(binding.value.parent) || document.body // TODO:得考慮沒有 binding.value.parent 的情況,重新登錄直接進(jìn)到內(nèi)頁會(huì)出現(xiàn) if (!tableStickyObj[uid]) { tableStickyObj[uid] = { uid, fixFunObj: {}, // 用于存放滾動(dòng)容器的監(jiān)聽scroll事件 setWidthFunObj: {}, // 用于存放頁面resize后重新計(jì)算head寬度事件 autoMoveFunObj: {}, // 用戶存放如果是DOM元素內(nèi)局部滾動(dòng)時(shí),document滾動(dòng)時(shí),fix布局的表頭也需要跟著document一起向上滾動(dòng) scrollDomRect: {}, headerRect: { top: 0, left: 0 }, fixed: {}, // 表格左浮動(dòng) fixedRight: {}, // 表格右浮動(dòng) // binding, // el, tableWrapDom: el.getElementsByClassName('el-table__body-wrapper')[0], scrollDom } } __STICKY_TABLE.watched({ el, binding, vnode, uid }) // 監(jiān)聽父級(jí)的某些變量 // 當(dāng)window resize時(shí) 重新計(jì)算設(shè)置表頭寬度,并將監(jiān)聽函數(shù)存入 監(jiān)聽函數(shù)對(duì)象中,方便移除監(jiān)聽事件 window.addEventListener('resize', (tableStickyObj[uid].setWidthFunObj = () => { __STICKY_TABLE.setHeadWidth({ el, uid, binding, eventType: 'resize' }) // 首先設(shè)置表頭寬度 }) ) // 給滾動(dòng)容器加scroll監(jiān)聽事件。并將監(jiān)聽函數(shù)存入 監(jiān)聽函數(shù)對(duì)象中,方便移除監(jiān)聽事件 scrollDom.addEventListener('scroll', (tableStickyObj[uid].fixFunObj = (e) => { __STICKY_TABLE.fixHead(scrollDom, el, uid, binding) })) }, // component 更新后。重新計(jì)算表頭寬度 componentUpdated (el, binding, vnode) { const uid = vnode.componentInstance._uid __STICKY_TABLE.setHeadWidth({ el, uid, binding, eventType: 'componentUpdated' }) }, // 節(jié)點(diǎn)取消綁定時(shí) 移除各項(xiàng)監(jiān)聽事件。 unbind (el, binding, vnode) { const uid = vnode.componentInstance._uid window.removeEventListener('resize', tableStickyObj[uid].setWidthFunObj) const scrollDom = document.querySelector(binding.value.parent) || document scrollDom.removeEventListener('scroll', tableStickyObj[uid].fixFunObj) if (binding.value.parent) { document.removeEventListener('scroll', tableStickyObj[uid].autoMoveFunObj) } } })
到此這篇關(guān)于vue中el-table實(shí)現(xiàn)自動(dòng)吸頂效果(支持fixed)的文章就介紹到這了,更多相關(guān)el-table 自動(dòng)吸頂內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決vue中修改export default中腳本報(bào)一大堆錯(cuò)的問題
今天小編就為大家分享一篇解決vue中修改export default中腳本報(bào)一大堆錯(cuò)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08vue 父組件給子組件傳值子組件給父組件傳值的實(shí)例代碼
這篇文章主要介紹了vue 父組件給子組件傳值,子組件給父組件傳值,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04vue2/vue3路由權(quán)限管理的方法實(shí)例
最近用vue框架做了個(gè)后臺(tái)管理項(xiàng)目,涉及權(quán)限,所以下面這篇文章主要給大家介紹了關(guān)于vue2/vue3路由權(quán)限管理的相關(guān)資料,需要的朋友可以參考下2021-06-06vue的簡(jiǎn)介及@vue/cli?腳手架的使用示例
vue 是一個(gè) 漸進(jìn)式的javascript框架,腳手架是一個(gè)通用概念,幫助搭建項(xiàng)目的工具,本文以vue2為例結(jié)合實(shí)例代碼給大家詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧2022-12-12非Vuex實(shí)現(xiàn)的登錄狀態(tài)判斷封裝實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于非Vuex實(shí)現(xiàn)的登錄狀態(tài)判斷封裝的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02