H5实现固定表头首列表格

文章讲述了在开发过程中遇到的表格滚动问题,包括粘性布局在Android和iOS上的限制,最后采用将表格划分为表头、首列和表格体的策略,并通过JavaScript处理iOS的滚动事件以实现流畅滚动。
摘要由CSDN通过智能技术生成

一、开发历程

开发过程中试过

1、使用粘性布局和固定粘性定位,固定定位,但都存在一些问题,比如:

1.1、粘性布局在安卓手机(如oppo、vivo)自带的浏览器中会失效,表头并不固定

1.2、滑动的过程中,表格会有拖拽的效果(看起来脱离了文档流,会出现空白,但松开会回弹回去)

1.3、在1.2的基础上,固定定位的首列扔定位在原处,让页面看起来分离成了三部分

2、最终方案

将表格划分为三部分,表头、首列、表格体。其中表格体的滚动效果通过overfloe:scroll实现,表头、首列通过表格体的滚动事件来动态设置绝对定位的偏移值来实现。

二、实现

1、HTML

  <div class="content-table" v-show="tabActive == 2">
        <!-- 表头 -->
        <div class="rowHeader">
          <table class="rowHeader-table" id="rowHeader-table">
            <tr>
              <td class="td seller_name tableH_fixed-left">销售员</td>
              <td class="td seller_phone">手机号</td>
              <td class="td one_branch">一级分校</td>
              <td class="td two_branch">二级分校</td>
              <td class="td total_uv double">累计访问人数</td>
              <td class="td total_time double">累计访问时长(min)</td>
              <td class="td add_uv double">昨日新增访问人数</td>
              <td class="td add_time double">昨日新增访问时长(min)</td>
            </tr>
          </table>
        </div>
        <!-- 列头 -->
        <div class="columnHeader">
          <table class="columnHeader-table" id="columnHeader-table">
            <tr v-for="(item, index) in sellersList" :key="index">
              <td class="td seller_name">
                {{ item.sellerName }}
              </td>
            </tr>
          </table>
        </div>
        //表格滚动主体
        <div class="tableBody" id="tableBody" @scroll="scrollFn($event)">
          <table class="tableBody-table" id="tableBody-table">
            <tr v-for="(item, index) in sellersList" :key="index">
              <td class="td seller_phone">{{ item.sellerPhone }}</td>
              <td class="td one_branch">{{ item.branchName }}</td>
              <td class="td two_branch">{{ item.secondaryBranchName }}</td>
              <td class="td total_uv">{{ item.totalUv }}</td>
              <td class="td total_time">{{ item.totalVisitTime.toFixed(2) }}</td>
              <td class="td add_uv">{{ item.increasedUv }}</td>
              <td class="td add_time">{{ item.increasedVisitTime.toFixed(2) }}</td>
            </tr>
          </table>
        </div>
      </div>
    </div>

2、css

.content-table {
  position: absolute;
  left: 16px;
  top: 234px;
  width: 343px;
  height: calc(100% - 268px);
  // 表头
  .rowHeader {
    position: relative;
    width: 343px;
    max-width: 343px;
    overflow: hidden;
    left: 0px;
    // 表头table
    .rowHeader-table {
      position: relative;
      width: fit-content;
      height: 52px;
      border-spacing: 0px;
      .tableH_fixed-left {
        position: sticky;
        left: 0;
      }
      td {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 52px;
        color: #fff;
        font-size: 14px;
        background: #5b8ff9;
        border-right: 1px solid #fff;
        padding: 0px;
      }
      .seller_name {
        border-radius: 16px 0 0 0;
      }
    }
  }

  // 列头
  .columnHeader {
    position: relative;
    max-height: calc(100% - 55px);

    overflow: hidden;
    width: 80px;
    top: 0px;

    .columnHeader-table {
      position: relative;
      border-spacing: 0px;
      tr {
        background-color: #fff;
        box-sizing: border-box;
        td {
          font-weight: 600;
          border: 1px solid #f7f7f7;
          border-top: none;
          border-right: none;
        }
        &:first-child {
          td {
            padding: 0px;
            box-sizing: border-box;
          }
        }
      }
    }
  }

  // 表格本体
  .tableBody {
    width: 265px;
    max-height: calc(100% - 55px);
    position: absolute;
    top: 52px;
    left: 80px;
    overflow: auto;
    .tableBody-table {
      border-spacing: 0px;
      tr {
        height: 35px;
        &:nth-child(odd) {
          background: #e8f7ff;
          // border: 0.02667rem solid #f7f7f7;
        }
        &:nth-child(even) {
          background: #fff9ed;
        }
        td {
          height: 100%;
          font-size: 12px;
          text-align: center;
          // font-weight: 500;
          padding: 0px;
          border: 1px solid #f7f7f7;
          border-top: none;
          border-right: none;
          box-sizing: border-box;
          &:last-child {
            border-right: 1px solid #f7f7f7;
          }
        }
      }
    }
  }

  // 表格元素
  tr {
    width: fit-content;
    white-space: normal;
    display: flex;
    .td {
      display: block;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 35px;
      color: #262626;
      text-align: center;
      box-sizing: border-box;
    }
    .seller_name {
      width: 80px;
      box-sizing: border-box;
    }
    .seller_phone,
    .one_branch,
    .two_branch {
      width: 84px;
      text-align: center;
      box-sizing: border-box;
    }
    .total_uv,
    .total_time,
    .add_uv {
      width: 64px;
      text-align: center;
      box-sizing: border-box;
    }
    .add_time {
      width: 71px;
      text-align: center;
      box-sizing: border-box;
    }
  }
}

3、js实现表头、首列滚动

    scrollFn (event) {
      const col = document.getElementById('columnHeader-table')
      col.style.top = -event.target.scrollTop + 'px'
      const row = document.getElementById('rowHeader-table')
      row.style.left = -event.target.scrollLeft + 'px'
    },

4、遇到的bug调整

bug1:ios橡皮筋效果

解决方案:处理ios滚动的橡皮筋效果_多多的小宝贝的博客-CSDN博客

bug2:ios中纵向滚动、横向滚动不独立(横向滑动的同时,纵向会轻微滚动,纵向滑动的同时,横向会轻微滚动)

解决方案:禁用ios的自身滚动,利用代码实现滚动效果(但是滚动会没有浏览器滚动那么流畅丝滑,如果小伙伴们有更好的解决方案~可以一起讨论呐)

  async mounted () {
    if (/(iPhone|iPad|iPod)/i.test(navigator.userAgent)) {
      console.log('是iOS设备')
      // 阻止默认的处理方式(阻止下拉滑动的效果)
      // passive 参数不能省略,用来兼容ios和android
      const tableBody = document.getElementById('tableBody')
      // 开始滑动的
      let startPos, endPos, isScrolling, scrollTime
      tableBody.addEventListener('touchstart', function (e) {
        var touch = e.targetTouches[0] // touches数组对象获得屏幕上所有的touch,取第一个touch
        // 开始滑动的时间戳
        scrollTime = new Date().getTime()
        startPos = { x: touch.pageX, y: touch.pageY, time: +new Date() } // 取第一个touch的坐标值
        if (tableBody.offsetHeight > tableBody.offsetWidth) {
          e.preventDefault()
        }
      }, { passive: false })
      // 触摸移动
      tableBody.addEventListener('touchmove', function (e) {
        var touch = e.targetTouches[0]
        endPos = { x: touch.pageX - startPos.x, y: touch.pageY - startPos.y }
        isScrolling = Math.abs(endPos.x) < Math.abs(endPos.y) ? 1 : 0 // isScrolling为1时,表示纵向滑动,0为横向滑动
        if (isScrolling == 0) { // 横向
          // 横向滚动纵向不变
          const time = (new Date().getTime() - scrollTime) / 1000
          const speed = Math.abs(endPos.x) / time
          // 判断左滑还是右滑,在左端就禁止左滑
          if (endPos.x > 0 && tableBody.scrollLeft == 0 && endPos.x < startPos.x) { // 右滑
            console.log('左滑')
            e.preventDefault()
          } else {
            console.log('endPos.x ', endPos.x)
            if (endPos.x < 0) {
              tableBody.scrollTo({ left: `${tableBody.scrollLeft + speed * 0.7}`, behavior: 'smooth' })
            } else {
              tableBody.scrollTo({ left: `${tableBody.scrollLeft - speed * 0.7}`, behavior: 'smooth' })
            }
            const row = document.getElementById('rowHeader-table') // 表头
            row.style.left = -tableBody.scrollLeft + 'px'
          }
        }
        if (isScrolling == 1) { // 纵向
          const time = (new Date().getTime() - scrollTime) / 1000
          const speed = Math.abs(endPos.y) / time
          console.log('speed', speed)
          // console.log('hh_endPos.y', endPos.y) // 上滑<0,下滑》0
          if (tableBody.scrollTop == 0 && endPos.y > 0) {
            e.preventDefault()
          } else {
            // 纵向滚动横向不变
            if (endPos.y < 0) {
              tableBody.scrollTo({ top: `${tableBody.scrollTop + speed * 0.8}`, behavior: 'smooth' })
            } else {
              tableBody.scrollTo({ top: `${tableBody.scrollTop - speed * 0.8}`, behavior: 'smooth' })
            }
            const col = document.getElementById('columnHeader-table')// 首列
            col.style.top = -tableBody.scrollTop + 'px'
          }
        }
      }, { passive: false })
    }
  },

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值