Tables are quite a complicated thing. They have existed since the dawn of HTML and they bring with them some serious problems. They are great for showing little data but rather complicated when the data grows. I had to investigate this for a recent project of mine (related to how to use Medium stats. The solution I have chosen involves the use of CSS and the Grid Layout Module. Here are my passages and my arguments.

But before starting an image with the result I want to achieve:

table-css-grid-00.gif

Introduction

The first step is to look for what already exists. There are some interesting articles worth reading:

From this I started to think about how to create my table.

First, of course, I need the data. I decided not to use fictional data for this article simply because the best way to learn is by solving a real problem. My problem is: I have some stats regarding my earnings on Medium. How can I analyze them?

But I won’t talk about the data itself. For the moment I just need to know how they are organized. And I decided to organize them into an array, listStories. Each element of this array is an object composed of these properties:

I don’t care about showing every single property. And I’m interested in having an easy way to decide which ones to view and order. To do this I need another array, this time made up of objects composed like this:

The properties are:

  • key, the property to show in the table
  • title, the name of the column
  • type, the type of data (numeric, date, string, boolean, …)
  • width, the width of the column. If not present it is interpreted as auto
  • align, the alignment of the column

Create a simple table

The table is in CSS. But to use it effectively I need something to automatically insert various data into the table. I decided to use Svelte: it allows me to keep the JavaScript code, the HTML5 part and the table styles in a single file. Also to speed up the writing of CSS I use the Tailwind CSS classes.

I start by creating the props to import the table data and labels:

I write the HTML part:

Obviously the result is very bad:

I need to add some styles to make it presentable. I start by defining everything as a CSS Grid:

Then I bold the first row, the one with the names of the various columns:

Finally I add a line to divide each row of the table:

The result is a little more elegant but still not useful:

I need to use the grid-template-columns property to define how many columns there should be.

A little while ago I explained how to create the headers prop. Well, the number of columns is simply the number of elements in the array.

Finally something like a table appears:

There’s a thing I don’t like: the columns are all the same size. It makes more sense to make some columns smaller and leave the one with the article title larger. To do this I use the width property of each label:

Now the table is a little prettier.

However, some critical issues remain. First, some values do not appear, others are in the wrong format. I add a function to solve them:

I edit the HTML

This fixes the wrong format:

In a similar way I can correct the alignment of the columns:

Keep the header visible

This is fine for tables with little data. But there is a problem when there are several rows of data. Scrolling down the header of the columns disappears, making it difficult to read. To keep the first line fixed I have to modify the structure of the HTML part and the CSS style. I start by adding a header tag and a section tag:

The idea is to set a maximum height for the section with the data lines and then add a side scrollbar to scroll through the data. To do this I first need to change the display property of article:

header and section instead become grid:

The section can have a vertical scrollbar.

To maintain the alignment of the last column, I should customize the scrollbar. For details I recommend reading CSS Almanac - Scrollbar:

I also shorten the width of the header:

The result of all this is a table with the first fixed row at the top:

table-css-grid-01.gif

Add a row for totals

Another thing I need is a line with the total. Because especially when I add a system of filters it is convenient to immediately see what the value of the selection is. To do this I add a footer to the table:

I know, I haven’t defined totals yet. But I have to decide where and how to calculate the totals. The simplest way is by adding a third props.

So I define a props of this type:

I correct the HTML code to format the values:

This allows me to get something like this:

Sort the data

A useful feature is the ability to sort the data.

There are various ways to achieve this. I can add buttons outside the table, or I can add a mouse-based control. I would like to order in ascending or descending order using a context menu.

Maybe I’ll talk about it in more depth in another post, but with Svelte it’s quite easy to create a context menu. For the moment I just recommend this repl:

For the moment I create a very simple component. First I need a couple of icons (SortAscending and SortDescending) to use as buttons.

Then a couple of props to manage the position on the screen:

It is also necessary to understand when the menu should be displayed and when it shouldn’t be:

I use createEventDispatcher to set the events I need:

Then I add events to hide the context menu when we click on some other element of the page:

Last, I add some CSS styles:

By combining everything I get:

Now all that remains is to insert it into the table. I decided to activate the context menu every time you click on a cell, not just in the column headings:

It’s a minimal context menu but it’s enough:

table-css-grid-02.gif

Nothing happens if I click on the icons. Because I haven’t linked any functions to the two commands. I need to go back to the table and add another prop:

I also need two functions to sort values in ascending and descending order:

Finally I update the HTML code

Now I can sort the various columns:

table-css-grid-03.gif

Add some colors

As far as it works, there remains a problem. It is not clearly visible which column we have selected, or on which row the mouse is positioned. To solve the problem I can use a few lines of CSS.

Let’s start with the lines. I think the quickest way is to add an element which contains all the elements of the row:

I adjust the styles to keep the same format:

And then, of course, I add a hover effect:

table-css-grid-04.gif

For columns I use the cellData variable to manage styles. First I make sure to avoid unwanted styles when the context menu is not visible:

Then I add a directive class:name

I add the style:

This allows me to make the context menu effects visible:

table-css-grid-05.gif

Add a chart

Numbers and words are fine but there is another aspect that interests me: the ability to graphically represent some values. It’s simple to add a bar chart. I use the space in the cell with the title.

I start by setting up a couple of props:

I want to color the lines proportionally to the indicated value. To do this, I need the maximum value:

Then I create a function to define the style:

Finally I modify the HTML code of the cells:

I get this:

table-css-grid-06.gif

Choose the data to show

I want to customize the choice of the column to use in the chart. I modify the context menu by adding another button:

I have to use something to signal when to show the button because not all data can be represented graphically. For example the dates, or the texts. I add a chartsColumns variable with the list of columns

Then I add a function to select the data:

There is a problem: how do I know which data I am viewing? There can be various ways. For the moment I think it is enough to highlight the column, perhaps using a bold font.

table-css-grid-07.gif

Add numbers to lines

One last detail remains: the line numbers. Simply add an index to Svelte’s #each loop:

Then I modify the gridTemplate variable to create the corresponding column:

It only takes a few lines of code to achieve this:

table-css-grid-08.gif

Animate the table

Finally I can add an animation to make it evident when we sort the table. To do this I use the animate:fn directive:

In this way I make the order operation visible:

table-css-grid-09.gif

Well, that’s all for now. I still have to think about if and how to manage filters and groupings. Maybe I’ll talk about it in the future.

As for the code, however, the repository I’m working on is el3um4s/medium-stats. It is a work in progress and the code is quite dirty. However, it can be useful.

Finally, these are my other articles related to Svelte and SvelteKit: