el-tableの横幅をコンテンツの幅に合わせるのに苦労した話


環境

  • Vue 2.6.11
  • Element UI 2.13.0

はじめに

HTMLのtableタグで作るテーブルの横幅はコンテンツの幅によって決まります。ですが、Element UIのテーブルは横幅が100%になるため、コンテンツの量が少ないとスカスカに見えてしまいます。そのため、HTMLのtableタグのようなテーブルにしたかったのですが、これが一筋縄ではいきませんでした。

要するにこういうのを

こうしたいということです。

テーブル全体の横幅の変更

テーブル全体の横幅はブロック要素をインライン要素に変更して、横幅の指定を初期化することによって縮めることができます。

Table.vue
.el-table {
  display: inline-block;
  width: initial;
  max-width: initial;
}

しかし、これだけではこんな風に縮みすぎてしまいます。

HTMLのtableタグであれば、white-space: nowrap;でセル内の折り返しを抑制できるのですが、el-tableの場合は列幅を自力で計算するしかありません。

テーブルの各列の横幅の変更

コンテンツの文字数に応じて列幅を設定するために、次のようなメソッドを作成します。

Table.vue
getColumnWidth (rows, headerTitle, columnName) {
  let maxLength = this.getLength(headerTitle)
  for (const row of rows) {
    const length = this.getLength(row[columnName])
    if (length > maxLength) maxLength = length
  }
  return maxLength * 8 + 20
}

ここで「8」は1文字の長さで「20」は左右の余白の幅です。また、全角文字は2文字とカウントしたいので、文字数を取得するメソッドも作成します。

Table.vue
getLength (str) {
  let length = 0
  for (let i = 0; i < str.length; i++) {
    const c = str.charCodeAt(i)
    if ((c >= 0x0 && c < 0x81) || (c === 0xf8f0) || (c >= 0xff61 && c < 0xffa0) || (c >= 0xf8f1 && c < 0xf8f4)) {
      length += 1
    } else {
      length += 2
    }
  }
  return length
}

ここまで作成ができたら、テーブルの各列で横幅を設定します。

Table.vue
<el-table-column :width="getColumnWidth(rows, '都道府県', 'prefecture')">
</el-table-column>

するとこうなります。

良い感じですね。もう一息です!

他の要素をテーブルの幅に合わせる

例ではページングの要素がテーブルから離れすぎているので、テーブルの右端に合わせたいです。ですが、テーブルの横幅は動的に計算しているので、事前には分かりません。こういう時はVueの$nextTick()を使ってDOM要素がレンダリングされた後にテーブルの横幅を取得して設定します。

Table.vue
search () {
  // テーブルに表示するデータを設定する処理
  // ・・・
  this.$nextTick(() => {
    let width = 0
    for (const child of this.$refs.table.$children) {
      if (child.width) width += child.width
    }
    this.$refs.paging.style.width = width + 'px'
  })
}

これで完成です!

さいごに

自分でやってみて、el-tableの標準機能でできない理由が何となく分かった気がします。。