? ? ? ? 在使用element-ui中,會發現表格組件el-table在未指定寬度情況下,會自動計算并給表格寬度賦值。但實際開發中,有時需要根據內容實際長度自動撐開顯示,由內容的多少而決定表格的寬度,而不是默認寬度為100%。在默認情況下表格寬度為100%顯示,并且列項未指定寬度情況下,剩余部分會平均分配寬度,如下圖:
圖1-默認情況寬度為100%
? ? ? ? 本案例實際需求如下圖所示,由內容自動撐開顯示。
圖2-根據內容自動撐開
一、演示頁面
? ? ? ? 首先創建Vue2項目,并新建演示頁面,頁面代碼如下:
<template><div><el-table size="mini" border :data="tableData"><el-table-column type="index" label="序號" width="50px"></el-table-column><el-table-column label="名稱" prop="name"></el-table-column><el-table-column label="內容" prop="content"></el-table-column><el-table-column label="圖片" prop="thumb"><template slot-scope="scope"><div class="thumb"><img v-if="scope.row.thumb" :src="scope.row.thumb" class="img" /></div></template></el-table-column><el-table-column label="創建時間" prop="createtime"></el-table-column><el-table-column label="更新時間" prop="updatetime"></el-table-column></el-table></div>
</template><script>
import AutoTable from './autoTable'
export default {data(){return {tableData: [{name: "Angular", content: "This is Angular Javascript", thumb: require("@/assets/angular.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "VueJs", content: "This is Vue Javascript", thumb: require("@/assets/logo.png"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "NuxtJs", content: "This is Nuxt Javascript", thumb: require("@/assets/nuxtjs.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "React", content: "This is React Javascript", thumb: require("@/assets/react.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "Dog", content: "This is Dog Javascript", thumb: require("@/assets/dog.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"}]}}
}
</script><style lang="less" scoped>
.thumb{position: relative;img{ display: block; }.img{width: 30px;}.hover{display: none;width: 200px;position: absolute;left: 35px;top: 0;z-index: 1000;}&:hover{.hover{ display: block; }}
}
</style>
? ? ? ? 以上代碼創建好后,顯示為“圖1”中的效果。
二、樣式調整
? ? ? ? 如下圖所示,element-ui中默認表格樣式.cell為塊級元素,為了能獲取到內容真實的寬度,這里需要將其修改為行內塊,這樣就能獲取到內容的寬度了。
? ? ? ? 這里使用的是less預編譯器,樣式代碼如下:
<style lang="less" scoped>
// 略.../deep/ .el-table .cell{ display: inline-block; }
/deep/ .el-table th.el-table__cell>.cell{ display: inline-block; width: auto; }
</style>
? ? ? ? 如上圖,樣式內容添加后,選擇元素則是按內容實際寬度區域進行顯示。
三、directives中定義鉤子函數
????????Vue.directive構子函數對于組件中元素的改造是非常方便的,此時需要添加一個可以自動處理表格寬度的自定義指令,用于統一處理項目中需要根據內容自動撐開的表格。
? ? ? ? 對于Vue.directive介紹之前一篇已介紹過,不清楚朋友可以前去了解一下,地址:Element-UI - 解決el-table中圖片懸浮被遮擋問題_vuetable的圖片放大后總是浮于文字下方-CSDN博客
? ? ? ? 由于本人項目將表格組件進行了封裝,所以這里使用局部自定義指令方法,如需全局處理可以將其功能移至全局文件中定義。
? ? ? ? 代碼如下:
export default {data(){return {tableData: [{name: "Angular", content: "This is Angular Javascript", thumb: require("@/assets/angular.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "VueJs", content: "This is Vue Javascript", thumb: require("@/assets/logo.png"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "NuxtJs", content: "This is Nuxt Javascript", thumb: require("@/assets/nuxtjs.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "React", content: "This is React Javascript", thumb: require("@/assets/react.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "Dog", content: "This is Dog Javascript", thumb: require("@/assets/dog.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"}]}},directives: {// 自定義v-autotableautotable: {inserted: (el, binding) => {}}},
}
? ? ? ? 注意:自定義指令中inserted鉤子函數是在被綁定元素插入父節點時調用,只執行一次。
四、定義AutoTable類
? ? ? ? 定義AutoTable類,用于獲取每列內容的實際寬度,并通過獲取每列中最大的內容寬度計算出表格的寬。
? ? ? ?代碼如下:
/*** 定義類 - 自定義表格寬度*/
class AutoTable{constructor(){this.timerHandle = null; //計時器手柄}// 開始重新計算表格寬度start(el, binding){}
}
export default AutoTable;
? ? ? ? 本類計時器是用于延遲獲取內容部分DOM,當內容讀取到后通過或滿足某些條件后,通過this.timerHandle進行清除計時器。
五、修改表格寬度
????????如下圖可見,element-ui中的表格組件,是分頭部標題部分和內容部分。所以要分兩部分獲取,先獲取頭部對應列內容的寬度,再獲取內容部分的列中所有內容的寬度,合并到一個數組中,篩選出每列中的最大寬度作為列寬。
? ? ? ? 下面實現原理和邏輯就不作細說了,每行代碼都已添加備注并說明作用,大家可以慢慢細品。代碼如下:
/*** 定義類 - 修正表格寬度*/
class AutoTable{constructor(){this.timerHandle = null; //計時器手柄}// 開始重新計算表格寬度start(el, binding){// 如果值為false, 則不進行計算處理if(!binding.value) return;const headerDOM = el.querySelector('.el-table__header-wrapper'), // 頭部DOMbodyDOM = el.querySelector('.el-table__body-wrapper'); // 內容DOM// 先清除舊計時器clearInterval(this.timerHandle);// 開始計時this.timerHandle = setInterval(() => {const tds = bodyDOM.querySelectorAll('table tbody tr'), // 獲取內容行數ths = headerDOM.querySelectorAll('table thead th .cell'), // 獲取頭部列元素emptyDOM = el.querySelector('.el-table__empty-text'); // 獲取空DOM節點// 如果滿足條件,結束計時器if(null != emptyDOM || tds.length>0) {clearInterval(this.timerHandle);}if(tds.length == 0 && ths.length == 0) return;// 定義變量 存儲每列的寬度數據const widths = []; // 二維數組,存儲行和列數據// 獲取標題中的單元格寬度ths.forEach((item, i) => widths[i] = [item.offsetWidth]);// 獲取內容部分列的單元格寬度tds.forEach(trEle => {const cells = trEle.querySelectorAll('td .cell');cells.forEach((item, i) => widths[i].push(item.offsetWidth))});// 定義存儲每列中最大值寬度數組const maxWidth = [];// 獲取每列中寬度最大值內容widths.forEach((arr, i) => maxWidth[i] = arr.reduce((maxValue, item) => Math.max(maxValue, item), arr[0]));// 定義變量存儲表格寬度(追加內填充10)const realWidth = maxWidth.reduce((total, value) => total + value, 0) + (maxWidth.length * 10);// 修改表格寬度if(realWidth>0) el.style.width = realWidth + 'px';}, 50);}
}export default AutoTable;
? ? ? ? 頁面中在el-table標簽上添加v-autotable,并且引入autotable.js文件,在組件注冊插入時,實例類對象并執行start()函數,代碼如下:
<template><div><el-table size="mini" border :data="tableData" v-autotable="true"><el-table-column type="index" label="序號" width="50px"></el-table-column><el-table-column label="名稱" prop="name"></el-table-column><el-table-column label="內容" prop="content"></el-table-column><el-table-column label="圖片" prop="thumb"><template slot-scope="scope"><div class="thumb"><img v-if="scope.row.thumb" :src="scope.row.thumb" class="img" /></div></template></el-table-column><el-table-column label="創建時間" prop="createtime"></el-table-column><el-table-column label="更新時間" prop="updatetime"></el-table-column></el-table></div>
</template><script>
import AutoTable from './autoTable'
export default {data(){return {tableData: [{name: "Angular", content: "This is Angular Javascript", thumb: require("@/assets/angular.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "VueJs", content: "This is Vue Javascript", thumb: require("@/assets/logo.png"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "NuxtJs", content: "This is Nuxt Javascript", thumb: require("@/assets/nuxtjs.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "React", content: "This is React Javascript", thumb: require("@/assets/react.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "Dog", content: "This is Dog Javascript", thumb: require("@/assets/dog.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"}]}},directives: {// 自定義懸浮v-autotableautotable: {inserted: (el, binding) => new AutoTable().start(el, binding),}},
}
</script>
? ? ? ? 此時頁面效果如下圖,細心朋友會發現此時未指定列寬部分的列,是等寬顯示并出現換行的情況,并未按每列最大實際寬度顯示,這個問題下面再細講。
六、colgroup
? ? ? ? 在解決列寬問題前,先來了解下colgroup。< colgroup > 元素用于設置表的特定列的樣式,< colgroup > 元素應該用作列規范的容器,每個組都使用 < col> 元素指定,span 屬性指定獲取樣式的列數,style 屬性指定列的樣式。
注意: < colgroup > 標記必須是一個 < table > 元素的子元素,并且應該放在任何其他表元素之前,如 < head > 、 < tr > 、 < td > 等,但是如果存在的話,應該放在 < caption > 元素之后。
????????在 colgroup 中允許使用的 CSS 屬性的選擇非常有限,合法 CSS 屬性如下:
名稱 | 描述 |
---|---|
width | 設置寬度 |
visibility | 是否可見 |
background | 背景樣式 |
border | 邊界線 |
????????所有其他 CSS 屬性對表沒有影響。
七、修整每列的寬度
? ? ? ? 如下圖可見,Element-ui組件中表格的列寬,是通過colgroup > col對每列寬進行控制的;所以要控制每列的寬度,則需要將其修正為之前獲取第列內容寬最大值即可。
? ? ? ? 代碼如下:
class AutoTable{// 略...// 開始重新計算表格寬度start(el, binding){// 如果值為false, 則不進行計算處理if(!binding.value) return;const headerDOM = el.querySelector('.el-table__header-wrapper'), // 頭部DOMbodyDOM = el.querySelector('.el-table__body-wrapper'); // 內容DOM// 先清除舊計時器clearInterval(this.timerHandle);// 開始計時this.timerHandle = setInterval(() => {const tds = bodyDOM.querySelectorAll('table tbody tr'), // 獲取內容行數ths = headerDOM.querySelectorAll('table thead th .cell'), // 獲取頭部列元素emptyDOM = el.querySelector('.el-table__empty-text'); // 獲取空DOM節點// 略...// 修改表格寬度if(realWidth>0) el.style.width = realWidth + 'px';// 獲取colgroupconst headerColGroup = headerDOM.querySelectorAll('colgroup col'),bodyColGroup = bodyDOM.querySelectorAll('colgroup col');// 修改每列寬度maxWidth.forEach((val, i) => {headerColGroup[i].style.width = val + 'px';bodyColGroup[i].style.width = val + 'px';});}, 50);}
}
? ? ? ? 通過獲取表格header中的colgroup > col 和body中的colgroup > col元素,再通過將第列中最大內容寬度賦值到對應列的col上即可。當上述代碼執行后,再查看col屬性則發現列的值已發生改變,如下圖:
? ? ? ? 頁面效果如下圖,此時第列中都以最大內容寬度顯示,表格寬度根據內容長度變化而改變。
八、更新寬度
? ? ? ? 由于inserted是在元素第一次插入時執行,只調用一次,所以當表格中數據變化,就需要使用到update和componentUpdated了。這里使用componentUpdated,它是在指令所在組件的 VNode 及其子 VNode 全部更新后調用。
? ? ? ? 代碼如下:
import AutoTable from './autoTable'
export default {data(){return {tableData: [{name: "Angular", content: "This is Angular Javascript", thumb: require("@/assets/angular.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "VueJs", content: "This is Vue Javascript", thumb: require("@/assets/logo.png"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "NuxtJs", content: "This is Nuxt Javascript", thumb: require("@/assets/nuxtjs.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "React", content: "This is React Javascript", thumb: require("@/assets/react.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"},{name: "Dog", content: "This is Dog Javascript", thumb: require("@/assets/dog.jpg"), createtime: "2024/6/15", updatetime: "2024/6/15"}]}},directives: {// 自定義懸浮v-autotableautotable: {inserted: (el, binding) => new AutoTable().start(el, binding),componentUpdated: (el, binding) => new AutoTable().start(el, binding)}},
}
? ? ? ? 雖然此方法不是真正意義上根據內容寬度自動撐開的,但也達到了預期中的效果,希望對大家有所幫助。