写在前面

本文主要介绍关于JSVue中如何进行表头,列固定,可以根据实际应用场景应用于原生Vue移动端小程序

实际效果展示:

在这里插入图片描述

对于列的固定,table中有对应的方法,但是如果列和表头都要固定,只能通过其他方式实现,如果您找到了更好的自身方法,还请斧正

思路概述

表头,列头,表格本身,这三个内容被分为了三块元素,将这三个内容进行抽离,并且通过定位和大盒子的包裹进行样式管理。

【当要进行滚动时,只要对滚动事件进行绑定,让表头和列头显示对应的内容就完成了滚动的操作

下面有Vue组件的和JavaScript原生的代码,可以直接运行查看逻辑和效果

具体实现

整体代码,可以直接创建组件,将代码复制到组件中使用,在App中引用运行【基于vue3+ts+less实现,其他版本会在后面给出修改思路】【最下面也提供了原生写法】

<!--
  ~ Time:2022/8/9 13:06 29
  ~ Name:fixmeTable.vue
  ~ Path:src/components/table
  ~ ProjectName:element-plus
  ~ Author:charlatan
  ~
  ~  Il n'ya qu'un héroïsme au monde :
  ~     c'est de voir le monde tel qu'il est et de l'aimer.
  -->
<script lang="ts" setup>
import { ref } from 'vue'

const row = 20
const column = 30

let top = ref('0px')
let left = ref('0px')
const Scroll = (event: Event) => {
  top.value = -event.target.scrollTop + 'px'
  left.value = -event.target.scrollLeft + 'px'
}
</script>
<template>
  <h3>
    列头表头固定table
  </h3>
  <hr>
  <!--  表格-->
  <div class="table">
    <!--    表头-->
    <div class="rowHeader">
      <table class="rowHeader-table">
        <tr>
          <td class="td" v-for="(item,index) in row" :key="index">
            {{ item }}
          </td>
        </tr>
      </table>
    </div>
    <!--    列头-->
    <div class="columnHeader">
      <table class="columnHeader-table">
        <tr v-for="(RootItem,index) in column" :key="index">
          <td class="td">
            {{ String.fromCharCode(RootItem + 64) }}
          </td>
        </tr>
      </table>
    </div>
    <!--    表格-->
    <div class="tableBody" @scroll="Scroll($event)">
      <table class="tableBody-table">
        <tr v-for="(RootItem,index) in column" :key="index">
          <td
              class="td" v-for="(item,index) in row" :key="index">
            {{ item + String.fromCharCode(RootItem + 64) }}
          </td>
        </tr>
      </table>
    </div>
  </div>
</template>
<style scoped lang="less">
.table {
  border: 1px solid red;
  padding: 1rem;
  border-radius: 1rem;
  position: absolute;
  left: 200px;
  top: 100px;
  width: 300px;
  height: 300px;

  // 表头
  .rowHeader {
    position: relative;
    width: 260px;
    max-width: 300px;
    overflow: hidden;
    left: 30px;

    .rowHeader-table {
      position: relative;
      left: v-bind(left);
      width: fit-content;
    }
  }

  // 列头
  .columnHeader {
    position: relative;
    max-height: 300px;
    overflow: hidden;
    width: 30px;
    top: 3px;

    .columnHeader-table {
      position: relative;
      top: v-bind(top)
    }
  }

  // 表格本体
  .tableBody {
    width: 265px;
    height: 300px;
    position: absolute;
    top: 55px;
    left: 50px;
    overflow: auto;
  }

  // 表格元素
  tr {
    width: fit-content;
    white-space: normal;
    display: flex;

    .td {
      display: block;
      width: 30px;
      height: 30px;
      text-align: center;
      line-height: 30px;
    }
  }
}
</style>
  1. html部分:主要进行框架的设计,这里表头,列头,表格本体分为三份

    <template>
      <h3>
        列头表头固定table
      </h3>
      <hr>
      <!--  表格-->
      <div class="table">
          
        <!--    表头-->
        <div class="rowHeader">
          <table class="rowHeader-table">
            <tr>
              <td class="td" v-for="(item,index) in row" :key="index">
                {{ item }}
              </td>
            </tr>
          </table>
        </div>
          
        <!--    列头-->
        <div class="columnHeader">
          <table class="columnHeader-table">
            <tr v-for="(RootItem,index) in column" :key="index">
              <td class="td">
                {{ String.fromCharCode(RootItem + 64) }}
              </td>
            </tr>
          </table>
        </div>
          
        <!--    表格-->
        <div class="tableBody" @scroll="Scroll($event)">
          <table class="tableBody-table">
            <tr v-for="(RootItem,index) in column" :key="index">
              <td
                  class="td" v-for="(item,index) in row" :key="index">
                {{ item + String.fromCharCode(RootItem + 64) }}
              </td>
            </tr>
          </table>
        </div>
      </div>
    </template>
    

    内容分为了三部分,这三部分将相互独立,通过样式的调节将三部分内容进行统一的管理。

    这里使用的是table进行了内容的填充,因为表头和列头是单独的元素,所以可以使用其他内容如div等,通过样式上的和交互上的进行协调统一,这里不在赘述

  2. 样式上的内容,因为主要是功能,这里样式使用less进行简单绘制

    <style scoped lang="less">
    .table {
      border: 1px solid red;
      padding: 1rem;
      border-radius: 1rem;
      position: absolute;
      left: 200px;
      top: 100px;
      width: 300px;
      height: 300px;
    
      // 表头
      .rowHeader {
        position: relative;
        width: 260px;
        max-width: 300px;
        overflow: hidden;
        left: 30px;
    
        .rowHeader-table {
          position: relative;
          left: v-bind(left);
          width: fit-content;
        }
      }
    
      // 列头
      .columnHeader {
        position: relative;
        max-height: 300px;
        overflow: hidden;
        width: 30px;
        top: 3px;
    
        .columnHeader-table {
          position: relative;
          top: v-bind(top)
        }
      }
    
      // 表格本体
      .tableBody {
        width: 265px;
        height: 300px;
        position: absolute;
        top: 55px;
        left: 50px;
        overflow: auto;
      }
    
      // 表格元素
      tr {
        width: fit-content;
        white-space: normal;
        display: flex;
    
        .td {
          display: block;
          width: 30px;
          height: 30px;
          text-align: center;
          line-height: 30px;
        }
      }
    }
    </style>
    

    内容上和定义的结构进行统一调节,这里需要主要的是:因为表格的宽度和表头的宽度对应,表格的高度和列头的高度固定,所以建议在定义时考虑好对应的宽高对应关系,方便调节

    在定义好表头和列头时,对与展示的部分通过外部的div进行定位的调节进行展示,内部的表格部分通过宽度的固定来达到样式统一,使用相对定位来完成展示内容的切换

    内部使用了Vue3提供的v-bind,如果你使用的时vue2或其他内容请更换为内联样式或其他方式

    v-bind为Vue3中提供,主要是将绑定的内容转换为自定义标签使用,具体介绍可以看我的另一篇文章:Vue3新属性 — v-bind in css,里面有用法和原理的详细介绍

  3. 逻辑部分:主要为内部的真正的表格绑定了滚动的监听,

    <script lang="ts" setup>
    import { ref } from 'vue'
    
    // 定义行和列
    const row = 20
    const column = 30
    
    // 将内容进行响应式定义
    let top = ref('0px')
    let left = ref('0px')
    
    // 绑定对应的监听事件
    const Scroll = (event: Event) => {
      top.value = -event.target.scrollTop + 'px'
      left.value = -event.target.scrollLeft + 'px'
    }
    </script>
    

    这里使用的动态数据在v-bind中进行,这里直接修改即可

其他环境中要修改的内容

这里对常见问题进行解决,如果没有匹配,请评论或找其他方式解决

  1. 原生JavaScript中使用:如果你完全没有使用过Vue,上面的代码我也改成了原生的版本,逻辑一样,这里就只放代码不做介绍了直接创建文件复制使用

    <!--
      ~ Time:2022/8/12 9:02 36
      ~ Name:test1.html
      ~ Path:
      ~ ProjectName:element-plus
      ~ Author:charlatan
      ~
      ~  Il n'ya qu'un héroïsme au monde :
      ~     c'est de voir le monde tel qu'il est et de l'aimer.
      -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<meta content="IE=edge" http-equiv="X-UA-Compatible">
    	<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    	<title>Title</title>
    	<style>
    		* {
    			margin: 0;
    			padding: 0;
    			list-style: none;
    			font: 500 15px YouYuan;
    			text-decoration: none;
    		}
    
    		li {
    			cursor: pointer;
    			position: relative;
    		}
    
    		/*-------以上为默认设置(The above is the default setting)--------*/
    
    
    		.table {
    			border: 1px solid red;
    			padding: 1rem;
    			border-radius: 1rem;
    			position: absolute;
    			left: 200px;
    			top: 100px;
    			width: 300px;
    			height: 300px;
    		}
    
    		.rowHeader {
    			position: relative;
    			width: 260px;
    			max-width: 300px;
    			overflow: hidden;
    			left: 30px;
    		}
    
    		.rowHeader-table {
    			position: relative;
    			left: 0;
    			width: fit-content;
    		}
    
    		.columnHeader {
    			position: relative;
    			max-height: 270px;
    			overflow: hidden;
    			width: 30px;
    			top: 3px;
    		}
    
    		.columnHeader-table {
    			position: relative;
    			top: 0;
    		}
    
    		.tableBody {
    			width: 265px;
    			height: 270px;
    			position: absolute;
    			top: 55px;
    			left: 50px;
    			overflow: auto;
    		}
    
    		tr {
    			width: fit-content;
    			white-space: normal;
    			display: flex;
    
    
    		}
    
    		.td {
    			display: block;
    			width: 30px;
    			height: 30px;
    			text-align: center;
    			line-height: 30px;
    		}
    	</style>
    </head>
    <body>
    <div class="table">
    	<!--    表头-->
    	<div class="rowHeader">
    		<table class="rowHeader-table">
    			<tr id="rowHeader-table-tr"></tr>
    		</table>
    	</div>
    	<!--    列头-->
    	<div class="columnHeader">
    		<table class="columnHeader-table" id="columnHeader-table-tr"></table>
    	</div>
    	<!--    表格-->
    	<div class="tableBody" id="scroll">
    		<table class="tableBody-table" id="tableBody-table-tr"></table>
    	</div>
    </div>
    </body>
    <script>
    	// 定义行和列
    	const row = 30
    	const column = 30
    
    	// 获取表头
    	let rowHeard = document.getElementById('rowHeader-table-tr')
    	// 创建表头元素
    	for (let i = 0, len = row; i < len; i++) {
    		// 获取元素
    		let td = document.createElement('td')
    		// 添加样式
    		td.classList.add('td')
    		// 添加内容
    		let testNode = document.createTextNode(i)
    		td.appendChild(testNode)
    		// 将表头放置到指定位置
    		rowHeard.appendChild(td)
    	}
    
    	// 创建表头元素
    	let columnHeard = document.getElementById('columnHeader-table-tr')
    	// 创建元素
    	for (let i = 0, len = column; i < len; i++) {
    		let tr = document.createElement('tr')
    		let td = document.createElement('td')
    		td.classList.add('td')
    		td.appendChild(document.createTextNode(String.fromCharCode(i + 64)))
    		tr.appendChild(td)
    		columnHeard.appendChild(tr)
    	}
    
    	// 创建表格元素
    	let table = document.getElementById('tableBody-table-tr')
    	for (let i = 0, len = column; i < len; i++) {
    		let tr = document.createElement('tr')
    		for (let j = 0, len = row; j < len; j++) {
    			let td = document.createElement('td')
    			td.classList.add('td')
    			td.appendChild(document.createTextNode(i + String.fromCharCode(j + 64)))
    			tr.appendChild(td)
    		}
    		table.appendChild(tr)
    	}
    
    	// 获取表头和列头滚动元素
    	let row_heard = document.getElementsByClassName('rowHeader-table')[0]
    	let column_heard = document.getElementsByClassName('columnHeader-table')[0]
    
    	// 绑定滚动时间
    	let scroll = document.getElementById('scroll')
    	scroll.onscroll = function (event) {
    		row_heard.style.left = -event.target.scrollLeft + 'px'
    		column_heard.style.top = -event.target.scrollTop + 'px'
    	}
    </script>
    </html>
    

    主要通过的是JS的元素创建然后将元素添加至对应的内容中,然后进行事件的绑定,从而完成整体逻辑

  2. React小程序中,二者都是可以进行数据的绑定,元素的循环处理更加方法,小程序中没有鼠标,可能要更换为手指滑动操作,对于这个逻辑相同,绑定元素的移动距离,从而让表头和列头跟随移动从而达到同步的效果和目的

本文仅代表本人粗鄙的拙见,如果有疏忽和纰漏,还请大佬斧正

Logo

前往低代码交流专区

更多推荐