Element UI實現表格列寬隨內容自適應

前言

對于動態獲取數據的表格,如果期望單元格內容不折行,就要設定足夠的寬度,同時又希望表格內容盡量緊湊,但是,由于數據不確定,所以無法預設寬度,怎么辦呢?有這樣一種辦法:

方案

  1. 先讓表格渲染
  2. 引入CSS class來讓單元格內容單行顯示
  3. 遍歷表體的.cell,算出每個元素的scrollWidth,匯總到二維數組里
  4. 考察二維數組的每個一級元素的每個單元格的寬度,找出最大值
  5. <el-table-column>設置的width都等于各自列的最大值即可

這個方案按理說,會發生表格閃爍,因為先渲染,又調整列寬的緣故。為了解決這個問題,我想到了visibility: hidden;,當hidden時,表格依然會渲染,只不過不顯示,此時就可以計算各種寬度,等計算好,賦值好,再visible即可。

恰好可以借用loading變量實現hiddenvisible的切換。

template

  1. 給表格組件加上這個:
ref="listTable" class="columns-fit" :class="loading ? null : 'visible'"
  1. 給每個<el-table-column>加上width,如下:
      <el-table-column type="selection" align="center" :width="colWidthList[0]" />
      <el-table-column label="序號" align="center" prop="id" :width="colWidthList[1]" />
      ...
      ...

其中下標表示第幾個列。你可以給有些列不設width,或者設置固定數值,此時其他列的下標無需調整,但是如果列順序變了,下標必須重排。

style

在某個全局引入的scss文件寫入:

.columns-fit {
  .el-table__header-wrapper, .el-table__body-wrapper {
    visibility: hidden;
  }

  &.visible {
    .el-table__header-wrapper, .el-table__body-wrapper {
      visibility: visible;
    }
  }

  .el-table__body-wrapper {
    overflow: auto;
  }

  td>.cell {
    display: inline-block;
    white-space: nowrap;
    width: auto;
    overflow: auto;
  }
}

script

先不說本方案,先說原始的請求數據列表的代碼大致是這樣:

    getList() {
      this.loading = true;
      list(this.queryParams).then(response => {
        this.list = response.data;
        this.total = response.total;
        this.loading = false;
      });
    },

然后我們對它略改造,加上一句:

    getList() {
      this.loading = true;
      list(this.queryParams).then(response => {
        this.list = response.data;
        this.total = response.total;
        this.$nextTick(() => {
          setTimeout(() => {
            this.colWidthList = this.$adjustColumnWidth(this.$refs['listTable'].$el);
            this.loading = false;
          });
        });
      });
    },

colWidthListdata里定義一個空數組,用來存每個col的最終寬度。

比較迷的是setTimeout,你是不是不知道我為啥加一句這個?當表格沒有橫向滾動條,用下方的代碼計算出來的每個單元格的scrollWidth會有錯誤,原因是Element UI的某些計算規則比較迷,它先計算一遍,然后微調一遍,會讓單元格稍微變化幾像素,而setTimeout從JS底層說是宏任務,可以等待Element UI對單元格的調整結束,這樣,得到的scrollWidth才是準的。

adjustColumnWidth函數需要寫入一個全局JS:

export default function(el) {
  let widthList = [];

  el.querySelectorAll('.el-table__body tr').forEach((tr) => {
    tr.querySelectorAll('td').forEach((td, i) => {
      if (!widthList[i]) {
        widthList[i] = [];
      }
      widthList[i].push(td.scrollWidth);
    });
  });

  return widthList.map(width => Math.max(...width));
}

main.js里引入:

import adjustColumnWidth from '@/utils/adjustColumnWidth';
Vue.prototype.$adjustColumnWidth = adjustColumnWidth;

到此OK。

使用特別說明

1. 本方案不考慮表頭的單元格溢出,請另外考慮

image.png

Element UI里面有這樣的樣式,其中由于text-overflow的值沒有none,也就是說,text-overflow一旦寫上了就無法取消,導致表頭無法像表身一樣呈現單行且無省略號的狀態,因此也就無法取得我們想要的表頭單元格的scrollWidth,所以,本方案不計算表頭單元格的寬度。這就導致了一個問題:

如果某列的表頭字符很長,但表身內容很短,這樣計算得到的width會比表頭字符還要短,表頭會出現折行。

結論:要么,你就接受這種折行的設定,要么,就給col寫死固定的、足夠的width值。

2. 刪除列、調整列順序時,:width="colWidthList[n]"的下標要記得對應修改

請記得對應修改。下標應該永遠是列的排序序號。

3. 瀏覽器窗口由小窗拉大到大窗,表格寬度不變,右側出現空白,怎么解決?

首先說,width屬性是Element UI官方屬性,如果全部列都設置了width,那么官方也沒有辦法讓表格自適應容器寬度,所以這其實并不是本方案的鍋。

我這里提個解決方案:

監聽window.onsize,動態修改colWidthList,等比放大,代碼我就不寫了,因為本身這個需求就是極小概率出現的需求。

4. 不要給所有表格都用本方案

如果表格明顯內容稀松,就堅決不要使用本方案,因為沒必要。

5. 不要給所有列都用本方案

假如某列的內容忽長忽短,短的只有幾個字,長的有50個字,那么這一列顯然不適合使用自適應列寬,因為會造成大面積的空白,請給該列鎖定width

6. 本方案的缺點

本方案為了不讓表格抖動,造成了2個負面效果:

  1. 會讓表格消失幾十到幾百毫秒甚至幾秒,根據ajax請求速度而定。如果不采用本方案,表格只是被loading遮罩遮住幾十到幾百毫秒至幾秒,而且遮罩往往半透明,能看到表格的隱約內容,是一種具有“高級感”的設計。

  2. 設置width會導致大規模的UI回流和重繪,頁面會非常輕微、不易覺察的卡頓一下,不過好在Element UI做政企系統多,可以強迫用戶使用現代瀏覽器,所以問題很輕微。

總之,如果需求方對界面美觀比較在意,對輕微的、不易察覺的卡頓不太在乎,那么可以考慮本方案,如果追求極致流暢,則請不要使用本方案。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。