image.png

公司有了新的需求,就是需要一个头部固定的表格,查来查去,都觉得很麻烦,干脆自己写一个好了

如第一幅图所示,效果很好,由于宽度是由浏览器窗口变化的时候通过js计算获得,css仅仅只是设置部分的最小宽度,所以兼容性肯定是没有任何问题的。


image.png

首先要明确的就是,既然头部需要固定,那么头部和身体肯定是分成两个表格来做的,结构如下

<div>
 <div class="table1">
  <table></table>
 </div>
 <div class="table2">
  <table></table>
 </div>
</div>

然后问题来了,由于是两个table,那么他们的td的宽度是由各自的内容决定的,自然宽度在不经过人为操纵的情况是绝对不可能一样的。既然如此,那通过同一个对象管理两个表格,那么宽度自然就相同了。

先看ts代码内容

  tabHead: Array<any> = [
    { title: '操作', property: 'checkbox', type: 'checkbox', width: '' },
    { title: '序号', property: 'serial', type: 'serial', width: '' },
    { title: '信息类型', property: 'infoType', type: 'special', children: 'infoTypeChildren', param: 'infoTypeCode', width: '', fackItem: 'a' },
    { title: '服务名称', property: 'serviceName', type: 'special', children: 'serviceChildren', param: 'serviceId', width: '', fackItem: 'b' },
    { title: "数据来源", property: 'sourceName', type: 'special', children: 'sourceChildren', param: 'dataSourceId', width: '', fackItem: 'c' },
    { title: "服务版本", property: 'serviceVersion', width: '' },
    { title: "服务描述", property: 'description', width: '' },
    { title: "注册单位", property: 'register', width: '' },
    { title: "注册时间", property: 'registerDate', width: '' },
    { title: "服务状态", property: 'status', type: 'state', width: '' },
  ]

上面的是两个table共用的对象,其他的字段可以不用理,只需要看title property width三个字段就行

再看html代码内容

    <div class="table1">
      <table class="service-table" >
        <tr class="head">
          <ng-container *ngFor="let item of tabHead">
            <td [ngStyle]="{'width':item.width}" class="{{item.property}}">
              {{item.title}}
            </td>
          </ng-container>
        </tr>
      </table>
    </div>

这个是头部表格的简化后的代码,利用angular的*ngFor属性,遍历tabHead循环出td并且给td的样式width设置为tabHeadwidth的值。

      <div class="table2" >
        <table class="service-table">
            <tr *ngFor="let data of tabData;let i = index" #element>
              <ng-container *ngFor="let item of tabHead">
                 <td [ngStyle]="{'width':item.width}" class="{{item.property}}">
                   <span [class]="item.class" [style]="item.style" [innerHTML]="data[item.property]?data[item.property]:'--'"
                        [title]="data[item.property]?data[item.property]:'--'"></span>
                   </td>
              </ng-container>
            </tr>
        </table>
      </div>

同样,这个是身体表格简化后的代码,同样利用angular的*ngFor属性,遍历tabHead循环出td并且给td的样式width设置为tabHeadwidth的值。不过外层加了一层tableData的循环,其字段如下

export class ServiceContentListModel {
    dataSourceId?: string;
    sourceChildren?:Array<any>;
    description?: string;
    id?: string;
    infoType?: string;
    infoTypeCode?: string;
    register?: string;
    registerDate?: string;
    serialNumber?: number;
    serviceId?: string;
    serviceName?: string;
    serviceVersion?: string;
    sourceName?: string;
    status?: string;
    noneed?:boolean=false;
}

简而言之就是tabHeadproperty会对应tableData的字段,然后把对应上的显示到页面上。具体不表


这样一番操作,可以想到两张表格中td的宽度都是由同一个对象tableHead中的width属性决定的了,那么接下来只要通过拿到身体表格每一个td的宽度,然后赋值给tabHead就行了。

//由于我使用的是angular,所以以下代码带有angular的特性,当然道理是相通的,用vue甚至用原生js写法稍微变通一下
//也是可以完成的


@ViewChild('element') navListElement: ElementRef;//拿到tr的dom元素

 ngAfterViewInit() {//在子元素加载完成后执行
    this.setWidth()
    window.onresize = ($event) => {//浏览器窗口大小变化以后执行
      this.setWidth()
    }
  }
  
    setWidth() {
    setTimeout(() => {//延迟一个生命周期后进行,不然在页面渲染完成以后表格还是对不齐状态
      if (this.navListElement) {
        let children = this.navListElement.nativeElement.children;//拿到一个tr中所有td的集合
        let widthSum = 0//存放一个tr中所有td的宽度,用于计算每个td所占的百分比
        for (let i = 0; i < this.tabHead.length; i++) {//每一列宽度换算成像素宽度(除了最后一列)
          let item = children[i]
          // this.tabHead[i]['width'] = item.offsetWidth + "px"
          widthSum+=item.offsetWidth
        }

        for (let i = 0; i < this.tabHead.length; i++) {//每一列宽度换算成百分比(除了最后一列)
          let item = children[i]
          this.tabHead[i]['width'] = (item.offsetWidth/widthSum)*100 + '%'
        }
      }
    });
  }

这样就完成了,具体点的工作,比如设置最小宽度之类的,直接在css里面设置就行。


munergs
30 声望8 粉丝

现在即是最好。