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:
The first step is to look for what already exists. There are some interesting articles worth reading:
- Responsive data tables with CSS Grid
- How to create responsive tables with pure CSS using Grid Layout Module
- Really Responsive Tables using CSS3 Flexbox
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
align, the alignment of the column
Create a simple table
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
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
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
section instead become
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
The result of all this is a table with the first fixed row at the top:
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 (
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:
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:
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:
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:
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:
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.
Add numbers to lines
One last detail remains: the line numbers. Simply add an index to Svelte’s
Then I modify the
gridTemplate variable to create the corresponding column:
It only takes a few lines of code to achieve this:
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:
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: