preface

Writing this article in the afternoon, breeze and sunshine, very comfortable 🏖. Then, without further ado, we’ll get straight to business. Today we’ll focus on the common problems with scalable columns, fixed columns, multilevel headers, and several tables ✍️, full of dry stuff 😯.

Tips: This article is following the last table component to understand? This article was written later, so I suggest you read the previous article at 👀. Of course, you can also look directly down, because here is the main idea, did not put much code 😁.

Scalable column

A scalable column, as the name suggests, is one that we can drag the table headerborderTo achieve column width, take a look at the following image 👇 :The meaning of the above figure should be expressed quite clearly, now let’s briefly look at the specific implementation steps 👇 :

The first step:

We add one more div to each th in the header, which is the red part in the figure above, and place it absolutely to the right of th.

The second step:

In the headerheaderAnd the table bodybodyI’m going to add one at the same leveldivTo represent the dotted lines in the image above, which are hidden by default, like this one:

Step 3:

Monitor the mouse events in the red part: when the mouse is pressed, the dotted line will be displayed and the left value of the dotted line will be changed in real time. When the mouse is raised, the dotted line will be hidden and the column width after dragging will be calculated. Then the width of the corresponding columns in the modified columns will be changed synchronously, so that the column width of the table head and body will be changed synchronously. Of course, remember to unbind mousemove and Mouseup events while the mouse is up, which is a good habit. That’s how scalable columns are implemented.

Fixed column

Let’s look at how a fixed column is implemented. We can add the fixed attribute to the columns. The value of the fixed attribute can be either left or right, as follows:

columns: [
    {
      title: 'name'.key: 'name'.width: 100.fixed: 'left'}... ]Copy the code

And just to simplify things, let’s just think about left column fixation, because right column fixation is the same thing.

Here is a quick explanation of the principle 😁 : it is to render one more table and position it absolutely on top of the original table. The following image should help you understand 👇 :In fact, the figure above is already the core of the implementation, So, we will directly talk about the specific implementation 😎 :

Step 1: Process tabular data

Because we want to put the columns that need to be fixed to the left, the first thing we need to do is to put all the columns that have fixed=”left” attributes in the table.

Step 2: Render two tables

In fact, table A and table B are identical, but table B has A few more properties, such as absolute positioning in the upper left corner, fixed width (width)fixed="left"Property column width sum), overflow hidden ah, etc. The specific code structure is shown in the figure below:In addition, for table AfixedPart of it we can setvisibility: hiddenBecause it doesn’t need to be shown, and Element is written that way. Likewise, for table B’s notfixedSections can also be setvisibility: hidden.

At this time I suddenly produced a question 🤔, isWhy is it set tovisibility: hiddenNot set todisplay: none?display: noneWouldn’t it be possible to render less DOM? Set tovisibilityWhat is the significance of?This is a good question and I suggest you think about it… Go down to 😁.

IView uses undefined: Hidden, and Ant Design uses undefined: hidden. This is very strange. So I changed the iView and Element’s visibility: hidden to display: None, and found that it seemed ok, and the table display was correct, so why? In fact, the main reason is that changing the visibility: hidden to display: none will cause problems in the distribution. What does that mean? If we set display: None, the row height in table A is not fixed, but table B does not display the contents of the body in table A, so table B cannot synchronize the height in table A. If we use visibility: hidden, tables A and B will both contain all data, but will not be visually visible, so that their row heights remain exactly the same, although this will result in extra DOM elements. So why is Ant Design ok? Ant Design also has this problem. Although it doesn’t render extra DOM, it calculates the row heights of table A beforehand and then synchronizes the row heights of fixed columns, as well as changes in table size and column width. They are two different scenarios, so feel it for yourself 🙌. Of course, here we use Element and iView.

Step 3: Synchronize scrolling

What is the problem with the above implementation 🤔? The most obvious problem is that tables A and B are split, so scrolling through one table does not respond to the other. In fact, the header and body of each table are separated, so now we need to scroll synchronously: when we scroll the body of table A horizontally, we need to scroll the header of table A synchronously; When we scroll the body of table A vertically, we need to scroll the body portion of table B simultaneously. A__body horizontal scrolling: obtain the scrollLeft value of A__body, and then synchronize the value to the table header of table A; A__body scroll vertically: Get the scrollTop value of A__body and synchronize the value to the body of table B. Of course rolling is a high frequency action, so we can do anti-shake treatment; You can also try scrolling with transform instead of scrollTop and scrollLeft.

Step 4: Synchronize hover

As with the third step, we need to synchronize each row like a scroll, that is, the hover style. Similarly, we use the body of hover table A as an example 🌰. Listen for the mouseEnter and mouseleave events of the row. When the mouse moves over A row, you can get the information of that row and then synchronize it to the corresponding row of the fixed column. In the same way, hover to A fixed column should also be synchronized to table A, which will not be described here.

Multistage header

header

We want to add a children to the columns as follows:

columns: [
    {
      title: 'date'.key: 'date'.width: 200
    },
    {
      title: 'Shipping Information'.children: [{title: 'name'.key: 'name'.width: 200
        },
        {
          title: 'address'.key: 'addr'}}]]Copy the code

From the above we can see that the multilevel header is actually rendered row by row, and each row can be rendered column by column, which is actually a two-dimensional array, twov-forLoop, let’s take a snapshot of the iView code to see the general structure:So the first thing we have to do is get it rightcolumnsFormat it to look like this (a two-dimensional array) :Each of these data nodes is given alevel,rowspancolspanThe latter two are used to implement merging cells, such ascolumnsThe first term date of itscolspanIt should bechildrenThe total, if notchildrenIs 1; It’srowspanShould be equal to the maximum level – current level +1, if anychildrenIt is 1.

This may be a bit convoluted, so we need to stop and think about it 🤔, but the essence of the transformation is data structure, so there is no special emphasis on how to transform, you can try it yourself 😊.

The table body

Body rendering is simple, but we also want tocolumnsTo do this, let’s see what the column looks like:In fact, the newcolumnsJust going through the old onescolumns, there arechildrenI’m just going to go through and get the subcolumns, nochildrenI just pull out that column, kind of like I got all the columns of the leaf nodes, and this new column will replace the old onecolumnsSince this data structure is easier to convert, I have posted the code here:

const getAllColumns = (cols, forTableHead = false) = > {
    const columns = deepCopy(cols);
    const result = [];
    columns.forEach((column) = > {
        if (column.children) {
            if (forTableHead) result.push(column);
            result.push.apply(result, getAllColumns(column.children, forTableHead));
        } else{ result.push(column); }});return result;
};
Copy the code

In fact, it is still a data conversion problem, we can see the following two pictures to deepen understanding 👇 : At this point, we are done with scalable columns, fixed columns, and multilevel header implementations, haha 😁😁😁.

Problem complement

1. Column width is inconsistent

In fact our table and table column widths are still not consistent, such as when the table body has a scrollbar, header, so there will be a scroll bar width of the gap, typically at this time when the vertical scroll bar is at the end of the header when add a column to fill the width difference, usually we call it a gutter. So what’s the width of this gutter? It’s basically the width of the scrollbar, so how do you calculate the width of the scrollbar? The idea is to create a div and use the width of the scroll bar (without overflow: Scroll width) – (overflow: Scroll width).

2, dislocation

In real development, if the table is out of place with Element for some reason, try the following methods:

this.$nextTick(() => {
    this.$refs['table'] && this.$refs['table'].doLayout()
})
Copy the code

3, caton

A screen full of tooltips and inputs can be a drag, as event listeners and DOM structures have increased, so avoid this. For example, display input only when the line is in edit state; Change tooltip to live render, don’t start with tooltip on everything.

4, the key value

V-for do not use index to bind key values, because index can change, so it is not reliable, try to use ID binding.

5. Big data rendering

It’s not unusual for a rendering of thousands of rows of data to get stuck, so if your table data is for pure presentation use functional: True to make it functional to reduce rendering overhead. Another is the virtual list, although thousands of form data, but the data is so much of visual area, range is so big, we will only need to render the viewing area and visual area near the up and down part of the data, other need not apply colours to a drawing, and then at the time of rolling according to how much to calculate the current scroll to display the data interval, That’s the easier said than done 😂.

summary

Ho ho, so far, we finally put the table component of the common function points finished, to tell the truth, is really trouble, but I hope to help you, hee hee 😄!