前言
對于動態獲取數據的表格,如果期望單元格內容不折行,就要設定足夠的寬度,同時又希望表格內容盡量緊湊,但是,由于數據不確定,所以無法預設寬度,怎么辦呢?有這樣一種辦法:
方案
- 先讓表格渲染
- 引入CSS class來讓單元格內容單行顯示
- 遍歷表體的.cell,算出每個元素的
scrollWidth
,匯總到二維數組里 - 考察二維數組的每個一級元素的每個單元格的寬度,找出最大值
-
<el-table-column>
設置的width
都等于各自列的最大值即可
這個方案按理說,會發生表格閃爍,因為先渲染,又調整列寬的緣故。為了解決這個問題,我想到了visibility: hidden;
,當hidden
時,表格依然會渲染,只不過不顯示,此時就可以計算各種寬度,等計算好,賦值好,再visible
即可。
恰好可以借用loading
變量實現hidden
和visible
的切換。
template
- 給表格組件加上這個:
ref="listTable" class="columns-fit" :class="loading ? null : 'visible'"
- 給每個
<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;
});
});
});
},
colWidthList
去data
里定義一個空數組,用來存每個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. 本方案不考慮表頭的單元格溢出,請另外考慮
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個負面效果:
會讓表格消失幾十到幾百毫秒甚至幾秒,根據ajax請求速度而定。如果不采用本方案,表格只是被loading遮罩遮住幾十到幾百毫秒至幾秒,而且遮罩往往半透明,能看到表格的隱約內容,是一種具有“高級感”的設計。
設置
width
會導致大規模的UI回流和重繪,頁面會非常輕微、不易覺察的卡頓一下,不過好在Element UI做政企系統多,可以強迫用戶使用現代瀏覽器,所以問題很輕微。
總之,如果需求方對界面美觀比較在意,對輕微的、不易察覺的卡頓不太在乎,那么可以考慮本方案,如果追求極致流暢,則請不要使用本方案。