自己造轮子之如何用HTML+JS搭建表头/列固定Table

        依旧是jQuery+html架构的前端项目,依旧用不了element.ui的组件。但产品已经提过无数遍想要表头固定、列固定的报表展示,没办法,只能借由排期不算太满的工作时间手肝一个HTML+JS的表头/列固定Table组件,命名为 fixedscrolltable

一、基本思路

        参考el-table交互可知,想要固定表头、固定列,需要将主表拆成4块:主表表头、主表表体;左侧列表头、左侧列表体。

         这里还补充了class名为table-leftmodel的div,是为了突出左侧列固定时的shadow。

 二、数据渲染

        确认四块区域后,接下来就是往里填数据。

        假如表头项是动态获取的,那么表单数据应该分为headers和rows:

2.1 拼接表头

        用到 isfixed 和 fixednum 传值,满足部分固定和随机列固定。

// 主表的表头
var header_html = '<tr>';
var fixheader_html = '';
// 固定列的表头
var leftheader_html = '<tr>';

for (var h = 0; h < header.length; h ++) {
    header_html += '<th>' + header[h] + '</th>';
    fixheader_html += '<div>' + header[h] + '</div>';
    if (isfixed == 'true' && h < fixednum) {
        leftheader_html += '<th>' + header[h] + '</th>';
    }
}
header_html += '</tr>';
leftheader_html += '</tr>';

2.2 拼接表体

        和拼接表头类似,搭配css样式设置body_html和leftbody_html。

        注意:如果列固定需要合并单元格,则需要提前处理rows,计算rowspan和colspan。

2.3 模块组装

        到这一步为止,仅展示数据。table自适应th和td导致的样式错乱还要再处理......

$(obj).empty().append(header_html).append(body_html); // 主表(包含表头)
$('#' + types + ' .table-box .boxhead').empty().append(fixheader_html); // 主表的表头
if (isfixed == 'true') {
     // true设置列固定,则显示固定列
     $('#' + types + ' .table-box .boxlefthead').empty().append(leftheader_html).show();
     $('#' + types + ' .table-box .boxleftbody').empty().append(leftbody_html).show();
} else {
     // true不设置列固定,只按固定表头展示
     $('#' + types + ' .table-box .boxlefthead').empty().hide();
     $('#' + types + ' .table-box .boxleftbody').empty().hide();
}

三、统一样式

        已知<table>标签存在css属性table-layout,这一属性的默认值为automatic —— 也就是自适应布局。简而言之,table内部的单元格会单元格内容自动拉伸,如果某行的td内容过长,那么相应列的宽度就会拉长。

        这个时候就要用到fiexed属性值 —— 给th(也就是table表头)设置宽度。

        由此展开思路,想要统一样式,首先得获取主表th的各项宽度,再赋值给其他三个模块。

3.1 获取主表表单的th宽度

$.each($('#' + types + 'boxbody tr:first th'), function(i, item) {
    var wid = $(item)[0].getBoundingClientRect().width.toFixed(3);
    var borderwidth = $(item).css('border-right-width').split('px')[0];
    borderwidth = Number(borderwidth);
    widthlist.push(wid - 12 - borderwidth);
});

知识点1:为什么使用getBoundingClientReact().width?

起先,通过width()方法获取元素宽度,此方法会自动四舍五入小数,导致渲染结果有细微错位。

故寻找另一种方法,也就是直接获取视图下元素的真实属性,包括内边距、边框。

知识点2:为什么要获取border-width?

 由于获取宽度时引用了视图维度的宽度值,所以需要兼容分辨率 —— 

3.2 将主表表单的th宽度赋值给固定列

        这里就不多做赘述,简言之就是用css()方法给需要固定的表单进行赋值。

        其中还有一个重要逻辑:为了容错,能用原始表单展示尽量用原始表单。比如,当数据不足以在固定高度内滚动,就不需要固定表头;当数据不足以在固定宽度内滚动,就不需要左侧固定列。这样也是为了避免浏览器兼容样式有误,导致表单变形。

var boxbodywidth = $('#' + types + 'boxbody')[0].getBoundingClientRect().width.toFixed(3);
var boxtablewidth = $('#' + types + 'boxbody .table')[0].getBoundingClientRect().width.toFixed(3);
var boxbodyheight = $('#' + types + 'boxbody')[0].getBoundingClientRect().height.toFixed(3);
var boxtableheight = $('#' + types + 'boxbody .table')[0].getBoundingClientRect().height.toFixed(3);
boxbodywidth = Number(boxbodywidth);
boxtablewidth = Number(boxtablewidth);
boxbodyheight = Number(boxbodyheight);
boxtableheight = Number(boxtableheight);
if (boxtablewidth > boxbodywidth || boxtableheight > boxbodyheight) {
   $.each($('#' + types + 'boxhead .table div'), function(i, kk) {
       $(kk).css('width', widthlist[i] + 'px');
   });
   $('#' + types + 'boxhead').css('width', (boxbodywidth - 6) + 'px');
   $('#' + types + 'boxhead').show();
} else {
   $('#' + types + 'boxhead').hide();
}

3.3 重置表单的滚动位置

        结合上面获取的变量,当表单重置前已经存在滚动时,需要通过scrollLeft和scrollTop手动设值;若不存在滚动,则不需要设置位置。

if (boxtablewidth > boxbodywidth) {
   $('#' + types + 'boxbody').scrollLeft() && $('#' + types + 'boxbody').scrollLeft(0) && $('#' + types + 'boxhead .table').scrollLeft(0);
   $('#' + types + 'boxbody').scrollTop() && $('#' + types + 'boxbody').scrollTop(0) && $('#' + types + 'boxhead .table').scrollTop(0);
}

四、监听滚动和页面可视区域

        至此,基本的表单样式已经展示完成。

        接下来的工作就是监听滚动,让表单能保持正常交互。

         在测试人员的惯用操作下,还补充了监听页面可视区域 —— 重置表单。

 五、总结

        显然,自己造轮子是一件很麻烦且很没有必要的事情。但研发必须得满足产品提出的合理的用户需求,所以与其一次次拖延,不如早点调研、早点尝试,即使过程中有太多样式BUG、交互BUG。

        所以,还是尽量不要用原始的框架。

        PS.文章中并未提及每块的css样式。

  • 15
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值