在html中实现table表格固定表头和首尾列的方法和文章很多,本文就不再赘述。
本文主要介绍vue项目中三种情景下实现table表格固定表头和首尾列
情景一:vue+element
只要在el-table元素中定义了height属性,即可实现固定表头的表格
通过设置max-height属性为 Table 指定最大高度。此时若表格所需的高度大于最大高度,则会显示一个滚动条。
固定列需要使用fixed属性,它接受 Boolean 值或者leftright,表示左边固定还是右边固定。
<template>
<el-table :data="tableData" style="width: 100%" max-height="250">
<el-table-column fixed prop="date" label="日期" width="150">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="province" label="省份" width="120">
</el-table-column>
<el-table-column prop="city" label="市区" width="120">
</el-table-column>
<el-table-column prop="address" label="地址" width="300">
</el-table-column>
<el-table-column prop="zip" label="邮编" width="120">
</el-table-column>
<el-table-column
fixed="right" label="操作" width="120">
<template slot-scope="scope">
<el-button
@click.native.prevent="deleteRow(scope.$index, tableData)"
type="text"
size="small">
移除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
methods: {
deleteRow(index, rows) {
rows.splice(index, 1);
}
},
data() {
return {
tableData: [{
date: '2016-05-03',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, ......, {
date: '2016-05-07',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}]
}
}
}
</script>
情景二:vue+quasar
<template>
<div class="q-pa-md">
<q-table
class="my-sticky-header-column-table"
title="Treats"
:data="data"
:columns="columns"
row-key="name"
/>
</div>
</template>
<script>
export default {
data () {
return {
columns: [
{name: 'name', required: true, label: 'Dessert (100g serving)', align: 'left', field: row => row.name, format: val => `${val}`, sortable: tru},
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true},
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs', sortable: true },
{ name: 'protein', label: 'Protein (g)', field: 'protein', sortable: true},
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium', sortable: true},
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)},
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)}],
data: [{ name: 'Frozen Yogurt', calories: 159, fat: 6.0, carbs: 24, protein: 4.0, sodium: 87, calcium: '14%', iron: '1%'},
{ name: 'Ice cream sandwich', calories: 237, fat: 9.0, carbs: 37, protein: 4.3, sodium: 129, calcium: '8%', iron: '1%'},
{ name: 'Eclair', calories: 262, fat: 16.0, carbs: 23, protein: 6.0, sodium: 337, calcium: '6%', iron: '7%'},
{ name: 'Cupcake', calories: 305, fat: 3.7, carbs: 67, protein: 4.3, sodium: 413, calcium: '3%', iron: '8%'},
{ name: 'Gingerbread', calories: 356, fat: 16.0, carbs: 49, protein: 3.9, sodium: 327, calcium: '7%', iron: '16%'},
{ name: 'Jelly bean', calories: 375, fat: 0.0, carbs: 94, protein: 0.0, sodium: 50, calcium: '0%', iron: '0%'},
{ name: 'Lollipop', calories: 392, fat: 0.2, carbs: 98, protein: 0, sodium: 38, calcium: '0%', iron: '2%'},
{ name: 'Honeycomb', calories: 408, fat: 3.2, carbs: 87, protein: 6.5, sodium: 562, calcium: '0%', iron: '45%'},
{ name: 'Donut', calories: 452, fat: 25.0, carbs: 51, protein: 4.9, sodium: 326, calcium: '2%', iron: '22%'},
{ name: 'KitKat', calories: 518, fat: 26.0, carbs: 65, protein: 7, sodium: 54, calcium: '12%', iron: '6%'}]
}
}
}
</script>
<style lang="sass">
.my-sticky-header-column-table{
/* height or max-height is important */
height: 310px
/* specifying max-width so the example can
highlight the sticky column on any browser window */
max-width: 600px
td:first-child{
/* bg color is important for td; just specify one */
background-color: #c1f4cd !important
}
tr th{
position: sticky
/* higher than z-index for td below */
z-index: 2
/* bg color is important; just specify one */
background: #fff
}
/* this will be the loading indicator */
thead tr:last-child th{
/* height of all previous header rows */
top: 48px
/* highest z-index */
z-index: 3
}
thead tr:first-child th{
top: 0
z-index: 1
}
tr:first-child th:first-child{
/* highest z-index */
z-index: 3
}
td:first-child{
z-index: 1
}
td:first-child, th:first-child{
position: sticky
left: 0
}
}
</style>
情景三:vue+自己封装的table组件或js的table标签
<template>
<!--overflow:auto为超出tableDiv的宽高才显示滚动条,宽高可以是通过动态计算得到的 -->
<div id="tableDiv" style="width:500px; height:400px;overflow:auto;margin:0 auto;margin-top:50px;">
<!-- table必须加transform这个样式,否则垂直滚动的时候,会有问题 -->
<table style="transform-style:preserve-3d;">
<thead>
<tr>
<th style="width:50px">Num</th>
<th style="width:120px">Name</th>
<th style="width:100px">Age</th>
<th style="width:300px">Address</th>
<th style="width:50px">Opt</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>张三</td>
<td>10</td>
<td>这是一条很长很长很长很长很长很长很长很长的文字</td>
<td>
<a href="">编辑</a>
</td>
</tr>
.......
<tr>
<td>12</td>
<td>张三12</td>
<td>10</td>
<td>这是一条很长很长很长很长很长很长很长很长的文字</td>
<td>
<a href="">编辑</a>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data () {
return {}
}
methods: {
// 因为要操作dom元素,在mounted中调用没问题,但是为避免在其他地方调用时dom还未加载,加个$nextTick()
this.$nextTick(() => {
fixtable () {
//这是外层div
var tableDiv = document.querySelector("#tableDiv");
//这是水平可滚动距离
var diff = tableDiv.scrollWidth - tableDiv.clientWidth;
//获取最后一列单元格,在这个例子里,最后一列是第5列
var lasts = tableDiv.querySelectorAll("tr td:nth-last-child(1),tr th:nth-last-child(1)");
//获取第一列列单元格
var firsts = tableDiv.querySelectorAll("tr td:nth-child(1),tr th:nth-child(1)");
//如果水平有滚动条,那一开始就需要让最后一列偏移
if (diff > 0) {
for (var i = 0; i < lasts.length; i++) {
lasts[i].style.transform = "translateX(-" + diff + "px)";
}
}
/*******固定的逻辑基本就下面这些*********/
var scroll_x = 0;
var scroll_y = 0;
tableDiv.addEventListener("scroll", function (e) {
//垂直滚动固定头
if (this.scrollTop != scroll_y) {
scroll_y = this.scrollTop;
this.querySelector("thead").style.transform = "translate3d(0," + this.scrollTop + "px,.1px)";
}
//水平滚动固定前两列和最后一列
if (this.scrollLeft != scroll_x) {
scroll_x = this.scrollLeft;
for (var i = 0; i < lasts.length; i++) {
lasts[i].style.transform = "translateX(-" + (diff - scroll_x) + "px)";
}
for (var i = 0; i < firsts.length; i++) {
firsts [i].style.transform = "translateX(" + scroll_x + "px)";
}
}
})
}
})
mounted() {
this.fixtable ()
}
}
}
</script>
<style>
table {
/*给table标签添加border-collapse: collapse属性,用于合并表格边框*/
border-collapse: collapse;
/*使用fixed实现th固定宽度,一般情况我们会让列宽自适应,可不设table-layout*/
table-layout: fixed;
/*在使用fixed的时候,必须指定width,否则还是自适应宽度*/
width: 100%;
border-spacing: 0;
}
th {
background: #888;
/*设个outline不然滚动会看着不协调*/
outline: 1px solid #333;
}
table th:nth-last-child(1){
background: #fff;
outline: 1px solid #333;
}
table th:nth-child(1){
background: #fff;
outline: 1px solid #333;
}
th,
td {
border: 1px solid #333;
background: #fff;
}
</style>