背景
产品要求表格的每一列的宽度为该列中的文字的长度,使其中的文字不换行.
技术方案
对table中的cell增加不换行滚动样式(注意样式穿透),让我们能够通过scrollWidth获得cell内的元素宽度
css
::v-deep(.cell) {
white-space: nowrap;
overflow: auto;
display: inline-block;
}
编写vue定义指令(v-fit-colums),获取table每一列元素的宽度的最大值,将该值设置为该一列的宽度
js
export default function fitColumns(app) {
app.directive('fit-columns', {
mounted(el) {
setTimeout(() => {
adjustColumnWidth(el)
}, 300)
},
updated(el) {
setTimeout(() => {
adjustColumnWidth(el)
}, 300)
},
unmounted(el) {
},
})
}
function adjustColumnWidth(table) {
console.log('2222333', table)
const colgroup = table.querySelector('colgroup')
const colDefs = [...colgroup.querySelectorAll('col')]
colDefs.forEach((col) => {
const clsName = col.getAttribute('name')
const cells = [
...table.querySelectorAll(`td.${clsName}`),
...table.querySelectorAll(`th.${clsName}`),
]
// 忽略加了"leave-alone"类的列
if (cells[0]?.classList?.contains?.('leave-alone')) {
return
}
const widthList = cells.map((el) => {
return el.querySelector('.cell')?.scrollWidth || 0
})
const max = Math.max(...widthList)
const padding = 32
table.querySelectorAll(`col[name=${clsName}]`).forEach((el) => {
el.setAttribute('width', max + padding)
})
})
}
但是由于公司的搜索栏是可收缩的,搜索栏一改变会导致table的大小发生改变(这个时候不会调用updated生命周期),因此需要给el添加resizeObserver,同时将resizeObserver注册到el中,方便unmounted的时候卸载
js
export default function fitColumns(app) {
app.directive('fit-columns', {
mounted(el) {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
setTimeout(() => {
adjustColumnWidth(entry.target)
}, 300)
}
})
resizeObserver.observe(el)
el.resizeObserver = resizeObserver
setTimeout(() => {
adjustColumnWidth(el)
}, 300)
},
updated(el) {
setTimeout(() => {
adjustColumnWidth(el)
}, 300)
},
unmounted(el) {
el.resizeObserver.unobserve(el)
el.resizeObserver = null
},
})
}
按照 参考文章中评论说到的 table表头在大屏幕中会错乱的问题,这是由于表头(.el-table__header-wrapper)的样式 overflow 为hidden,这个时候只需要将其设置为scroll,同时将滚动条隐藏
css
::v-deep(.el-table__header-wrapper) {
overflow: scroll;
scrollbar-width: none; /* Firefox 隐藏滚动条 */
-ms-overflow-style: none; /* IE 和 Edge 隐藏滚动条 */
&::-webkit-scrollbar {
display: none;
}
}
在table过宽的情况下,会出现滚动表单内容表头不会同时滚动,这个应该是element内部处理的问题,将header和body分开来,这个时候需要在v-fit-colums 中为table的表单内容绑定滚动事件(由于表头隐藏了不需要在表头绑定),同时采用requestAnimationFrame代替防抖进行性能优化
js
export default function fitColumns(app) {
app.directive('fit-columns', {
mounted(el) {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
setTimeout(() => {
adjustColumnWidth(entry.target)
}, 300)
}
})
resizeObserver.observe(el)
el.resizeObserver = resizeObserver
setTimeout(() => {
adjustColumnWidth(el)
}, 300)
const wrap = el.querySelector('.el-scrollbar__wrap')
const header = el.querySelector('.el-table__header-wrapper')
wrap.addEventListener('scroll', () => {
requestAnimationFrame(header.scrollTo(wrap.scrollLeft, 0))
})
},
updated(el) {
setTimeout(() => {
adjustColumnWidth(el)
}, 300)
},
unmounted(el) {
el.resizeObserver.unobserve(el)
el.resizeObserver = null
const wrap = el.querySelector('.el-scrollbar__wrap')
wrap.removeEventListener('scroll')
},
})
}