CSS trick: Centering an overflowed table
The trickiest HTML element to style is probably <table>
. The <table>
element
represents the older way of styling, and that’s much older than modern modules
such as flex
and grid
.
For example, here we get such a task: A <div>
element contains a <table>
.
The table may have many or only a few columns, so its width is uncertain.
We are asked to:
-
Add horizontal scroll to the table if its width is larger than the div.
-
Horizontally center the table if its width is smaller than the div.
Example HTML document:
<div class="mydiv">
<table class="mytable">
<thead>
...
</thead>
<tbody>
...
</tbody>
</table>
</div>
Failed solutions
This looks a pretty easy task, huh? Many designers know how to add horizontal
scroll to an element: overflow-x: scroll;
. And to horizontally center an
element, we have text-align: center;
(for inline elements) and margin: 0
auto;
(for block elements). So we can give some quick solutions.
Now let’s see where they fail.
Failed solution 1
Let’s first add the horizontal scroll:
.mytable {
overflow-x: scroll;
}
Problem:
- There’s no horizontal scroll.
Answer:
- By default, the
<table>
element hasdisplay: table;
.overflow-x: scroll;
doesn’t work with atable
display.
Failed solution 2
All right. Let’s display the table as block
.
.mytable {
overflow-x: scroll;
display: block;
}
Problem:
- Now the table has horizontal scroll. But it isn’t centered when there are
only a few columns. It isn’t centered even if we add
margin: 0 auto;
ortext-align: center;
.
Answer:
-
An element displayed as
block
will automatically expand to full width. So it can be seen as already centered, even though its contents aren’t. If you check the width of<thead>
and<tbody>
, you will find these elements are narrower than the<table>
element. -
Setting
text-align: center;
on the<table>
element will only center its descendants, because this CSS property should be set on the container element. Even so, it still has no effect on<table>
itself in this case because it has ablock
display. -
You may try other
display
properties (such asflex
andgrid
). However, you are likely to find the columns misalign. This is non-trivial to solve.
Correct solution
So what options do we have right now? Yes we have one more: display:
inline-block;
.
From Mozilla: display:
The element generates a block element box that will be flowed with surrounding content as if it were a single inline box (behaving much like a replaced element would).
So if we write:
.mytable {
overflow-x: scroll;
display: inline-block;
max-width: 100%;
}
Then horizontal scroll actually works. The remaining job is to center the
<table>
when it has a smaller width.
In this case, the <table>
element behaves much like an inline element to the
external. There are both good news and bad news resulting from this:
-
Good: It doesn’t expand to its parent’s full width.
-
Bad: It doesn’t respond to
margin: 0 auto;
, and there’s no easy way to usetext-align: center
.
Here’s where the position
CSS property comes to rescue. We use position:
relative;
on the <table>
element to horizontally center it.
The benefit of using position: relative;
is, the element doesn’t leave its
normal flow (this is different from using position: absolute;
), so we don’t
need another element to hold its place. From Mozilla: position:
The element is positioned according to the normal flow of the document, and then offset relative to itself based on the values of
top
,right
,bottom
, andleft
. The offset does not affect the position of any other elements; thus, the space given for the element in the page layout is the same as if position werestatic
.
We also use left: 50%
to place this element at half its parent’s width.
From Mozilla: left:
<percentage>
A
<percentage>
of the containing block’s width.
This is very close to the final result. The only problem now is, the element is
placed with its left boundary in the center of its parent, while what we
want is to place its center in the center of its parent. The next CSS
property we use is transform
. Specifically, we use transform: translate(-50%,
0%);
to move it back by 50% of its own width. This finally makes everything
good.
Check the final result here:
.mytable {
display: inline-block;
max-width: 100%;
position: relative;
left: 50%;
transform: translate(-50%, 0%);
overflow-x: scroll;
}
This solution not only is correct but also has a few advantages:
-
It is pure CSS solution, not using JS at all.
-
It doesn’t add any wrapper elements and keeps HTML document intact.
-
It may also work with elements other than
<table>
.
Caveat
Because we have used the position
property, this <table>
element now becomes
a positioned element, so its ordering (relative to other elements in the
page) may change. For example, if you have a fixed element (position: fixed;
)
without z-index
, then the <table>
element may display on top of it. This is
usually not what designers want because fixed elements are usually meant for
top-level display. To handle this, set a high z-index
on the fixed element.