vue perfectly realizes el-table column width adaptation

vue perfectly realizes el-table column width adaptation

background

Element UI is a popular Vue.js UI framework on PC. Its component library can basically meet most common business needs. But sometimes there are some highly customized requirements that the components themselves may not be able to meet. I encountered this recently in a project.

Many pages require the table component el-table. If no width is specified for el-table-column, it will be evenly distributed among the remaining columns by default. When there are many columns, if the width of el-table is limited to the container, the content in the cell will wrap. Force no line wrapping, the content will either scroll within the cell or overflow or be truncated.

The desired effect of the product is: the content is displayed in a single row, the column spacing is consistent, and the table exceeds the container to allow horizontal scrolling. el-table-column supports setting a fixed width, which can meet this requirement when the content width is predictable. The problem is how to make the column width dynamically adapt to the width of the content. I didn't find such an option in the official documentation, probably because the component itself does not support it.

Technical Solution

So I thought of a solution to dynamically calculate the content width. Some people have mentioned this idea online, and the approach is to calculate the width based on the number of characters in the content. This approach has several limitations:

  • Content must be text
  • Different characters have different widths, and the settlement results are not accurate enough
  • Need to manipulate data before rendering, which is not conducive to decoupling

I adopted another idea, which is to dynamically calculate the content width, but based on the actual rendered DOM element width, which can solve the above three problems.

How to do it specifically? By looking at the rendered DOM elements, it is found that the header and content of el-table use a native table respectively, and the width of each column is set by colgroup. Let's start from here. The name attribute value of col is consistent with the class value of the corresponding td. In this way, we can traverse all cells of the corresponding column, find the cell with the largest width, and use its content width plus a margin as the width of the column.

Specific implementation

How to calculate content width? This is a relatively critical step. After rendering, each cell has a .cell class, which is set to not allow line breaks with white-space: nowrap; overflow: auto;. The content can be scrolled if it exceeds the limit, and display: inline-block; is set to calculate the actual content width. In this way, the final width can be obtained through the scrollWidth property of the .cell element.

function adjustColumnWidth(table) {
  const colgroup = table.querySelector("colgroup");
  const colDefs = [...colgroup.querySelectorAll("col")];
  colDefs.forEach((col) => {
    const clsName = col.getAttribute("name");
    const cells = [
      ...table.querySelectorAll(`td.${clsName}`),
      ...table.querySelectorAll(`th.${clsName}`),
    ];
    // Ignore columns with the "leave-alone" class if (cells[0]?.classList?.contains?.("leave-alone")) {
      return;
    }
    const widthList = cells.map((el) => {
      return el.querySelector(".cell")?.scrollWidth || 0;
    });
    const max = Math.max(...widthList);
    const padding = 32;
    table.querySelectorAll(`col[name=${clsName}]`).forEach((el) => {
      el.setAttribute("width", max + padding);
    });
  });
}

The exploration process in the middle is rather cumbersome, but the final code implementation is very concise. When is the column width calculation triggered? Naturally, after the component rendering is completed. In order to facilitate reuse, I adopted the Vue custom directive approach.

Vue.directive("fit-columns", {
  update() {},
  bind() {},
  inserted(el) {
    setTimeout(() => {
      adjustColumnWidth(el);
    }, 300);
  },
  componentUpdated(el) {
    el.classList.add("r-table");
    setTimeout(() => {
      adjustColumnWidth(el);
    }, 300);
  },
  unbind() {},
});

Furthermore, I have encapsulated a Vue plug-in called v-fit-columns, which has been published to the npm repository and can be directly installed and used.
Install:

npm install v-fit-columns --save

Import:

import Vue from 'vue';
import Plugin from 'v-fit-columns';
Vue.use(Plugin);

use:

<el-table v-fit-columns>
  <el-table-column label="No." type="index" class-name="leave-alone"></el-table-column>
  <el-table-column label="Name" prop="name"></el-table-column>
  <el-table-column label="Age" prop="age"></el-table-column>
</el-table>

The source code repository is here: https://github.com/kaysonli/v-fit-columns. Welcome to give us your comments and stars!

Summarize

This solution is somewhat of a hack, and is only concerned with meeting the requirements. It may have some flaws in other aspects, such as a slight flash after rendering (because the width needs to be readjusted, which will cause reflow). However, judging from the final effect, it is quite satisfactory.

The above is the detailed content of vue's perfect implementation of el-table column width adaptation. For more information about vue's implementation of el-table column width adaptation, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Vue uses el-table to implement a circular carousel data list
  • Detailed explanation of the writing method of VUE2.0+ElementUI2.0 table el-table loop dynamic column rendering
  • The table in vue+Element is editable (select drop-down box)
  • Vue uses el-table to loop and generate tables

<<:  How to modify the firewall on a Linux server to allow remote access to the port

>>:  Windows 10 + mysql 8.0.11 zip installation tutorial detailed

Recommend

CSS achieves the effect of two elements blending (sticky effect)

I remember that a few years ago, there was an int...

Introduction to JavaScript built-in objects

Table of contents 1. Built-in objects 2. Math Obj...

A Deep Dive into the MySQL InnoDB Storage Engine

Preface In MySQL, InnoDB belongs to the storage e...

MySQL intercepts the sql statement of the string function

1. left(name,4) intercepts the 4 characters on th...

CSS3 realizes particle animation effect when matching kings

When coding, you will find that many things have ...

The use of FrameLayout in six layouts

Preface In the last issue, we explained LinearLay...

Copy and paste is the enemy of packaging

Before talking about OO, design patterns, and the ...

How to use webpack and rollup to package component libraries

Preface I made a loading style component before. ...

XHTML introductory tutorial: Use of list tags

Lists are used to list a series of similar or rela...

Example of disabling browser cache configuration in Vue project

When releasing a project, you will often encounte...

Docker binding fixed IP/cross-host container mutual access operation

Preface Previously, static IPs assigned using pip...

Node.js implements breakpoint resume

Table of contents Solution Analysis slice Resume ...

Summary of Vue watch monitoring methods

Table of contents 1. The role of watch in vue is ...

MySQL data backup and restore sample code

1. Data backup 1. Use mysqldump command to back u...

How to use Docker containers to implement proxy forwarding and data backup

Preface When we deploy applications to servers as...