粘性定位
-
滚动容器
具有
sticky
属性的元素,会固定在离它最近的一个拥有 滚动机制 的祖先元素上(overflow: auto)可以指定滚动容器的高度或最大高度来达到预期的效果
-
效果呈现
在一个滚动容器内
当元素跨越特定阈值前,显示的效果与
position:relative
一致当元素跨越特定阈值后,显示效果与
position:fixed
一致 -
注意事项
父元素不能设置
overflow: hidden
或overflow: auto
属性必须指定
top
,right
,bottom
或left
四个阈值其中之一,才可使粘性定位生效,否则其行为与相对定位相同
固定表头的表格
-
模板代码
<table class="table"> <thead :class="{ 'table-thead-fixed': isFixedThead }"> <tr> <th v-for="(column, index) of tableColumn" :key="index"> {{ column.label }} <th> </tr> </thead> <tbody> <tr v-for="(row, rowIndex) of tableData" :key="rowIndex"> <td v-for="(column, index) of tableColumn" :key="index" :class="{ [alignment[column.align || 'left']]: true }"> <template v-if="column.type === 'number'">{{ rowIndex + 1 }}</template> <template v-else-if="column.prop">{{ row[column.prop] }}</template> <template v-else-if="column.slot"> <slot :name="column.slot" :data="row"></slot> </template> </td> </tr> </tbody> </table>
-
脚本代码
export default { props: { tableColumn: { required: true, type: Array, }, tableData: { required: true, type: Array, }, isFixedThead: { default: true, type: Boolean, }, }, data () { return { alignment: Object.freeze({ 'left': 'text-left', 'center': 'text-center', 'right': 'text-right' }) } } }
-
风格代码
.table { width: 100%; table-layout: fixed; border-collapse: collapse; border-spacing: 0; font-size: 14px; color: #333; border-top: 1px solid #ebeef5; & th { padding: 12px 15px; text-align: center; font-weight: 500; background-color: #F5F7FC; } & td { padding: 8px 15px; } & td { &.text-left { text-align: left; } &.text-center { text-align: center; } &.text-right { text-align: right; } } & th, & td { border: 1px solid #ebeef5; } } .table-thead-fixed { position: sticky; top: 0; z-index: 20; }
表格初始化时,采用固定布局,各列宽度由第一行决定,后面指定的宽度会被忽略
减少表格渲染过程中的重绘,提升性能
遇到的问题
-
滚动时表格上方会缺少边框且还会有 1 像素的空隙
.table-supplement-style { & .table-thead-fixed::before { position: absolute; top: -1px; content: ''; width: 100%; height: 2px; background-color: #ebeef5; } }
const { contains, add, remove } = this.tableEl.classList if (scrollTop >= offsetTop) { if (!contains('table-supplement-style')) { add('table-supplement-style') } } else { remove('table-supplement-style') }
获取悬浮目标元素距离顶部的距离
offsetTop
通过监听滚动容器的
scroll
事件,获取滚动条距离顶部的距离scrollTop
通过比对,来给表格添加补充样式解决问题
-
表格的单元格设置了宽度,在悬浮效果下会失效,页面会有抖动感
<table class="table"> <colgroup> <col v-for="(column, index) of tableColumn" :key="index" :style="{ width: column.width ? column.width + 'px' : null }"> </colgroup> ... </table>
将表格的列宽通过
col
元素来设置,问题解决
固定列的表格(包括多列)
-
模板代码
<div class="scroll-container"> <table class="table"> <colgroup> <col v-for="(column, index) of tableColumn" :key="index" :style="{ width: column.width ? column.width + 'px' : null }"> </colgroup> <thead> <tr> <th v-for="(column, index) of tableColumn" :key="index" :class="{ 'table-column-fixed': column.fixed }" :style="getStickyLeftValue(index)"> {{ column.label }} </th> </tr> </thead> <tbody> <tr v-for="(row, rowIndex) of tableData" :key="rowIndex"> <td v-for="(column, index) of tableColumn" :key="index" :class="{ 'table-column-fixed': column.fixed, [alignment[column.align || 'left']]: true }" :style="getStickyLeftValue(index)"> <template v-if="column.type === 'number'"> {{ rowIndex + 1 }} </template> <template v-else-if="column.prop"> {{ row[column.prop] }} </template> <template v-else-if="column.slot"> <slot :name="column.slot" :data="row"></slot> </template> </td> </tr> </tbody> </table> </div>
-
脚本代码
export default { computed: { stickyLeftValue() { const fixedColumns = this.tableColumn.filter(column => column.fixed) let m = new Map(), len = fixedColumns.length while(len--) { const computedWidth = fixedColumns.slice(0, len).reduce((sum, column) => (sum += column.width), 0) m.set(len, computedWidth) } return m } }, methods: { getStickyLeftValue(index) { return { left: this.stickyLeftValue.get(index) + 'px' } } } }
通过计算属性算出固定列距离左边的偏移量,在渲染的时候通过索引获取
-
风格代码
.scroll-container { overflow-x: auto; } .table-column-fixed { position: sticky; z-index: 20; background-color: #fff; }
-
滚动时固定列的左右边框会消失
.table { border-top: 1px solid #ebeef5; border-right: 1px solid #ebeef5; th, td { position: relative; &::before, &::after { content: ''; position: absolute } &::before { left: 0; bottom: 0; width: 100%; height: 1px; background-color: #ebeef5; } &::after { left: 0; top: 0; height: 100%; width: 1px; background-color: #ebeef5; } } }
使用伪元素模拟左边框和下边框,并且给表格元素添加上边框和右边框
-
遇到的问题
问题: 滚动时左上角的交叉单元格会被右边和下边的滚动元素覆盖
解决: 设置层级 z-index 大于固定元素的层级即可解决
-
一起交流学习
加群交流看沸点
优化总结
基于原生的 CSS3
属性 position: sticky
实现的固定表头、固定列的表格,当数据达到一定量级的时候,在性能上会有很大的提升