Steve Wortham
Steve Wortham

Reputation: 22220

Layout a flex box similar to a table?

I'm working with a framework developed in-house which depends on a certain structure to our HTML. And one of the tricky things is that each row needs its own container with its own classes and data attributes.

So here's the problem. Without drastically changing the DOM, how can I make the flex box below render essentially like an HTML table would? Or is a table the only way? The solution will have to work in both IE11 and Chrome.

I'm trying to make it look like this...

Column A      |      Column B      |      Column C
1             |      2             |      3

section {
  display: flex;
  flex-wrap: wrap;
}

section .col {
  flex: 1 1 auto;
}

section .line-break {
  flex-basis: 100%;
  width: 0px; 
  height: 0px; 
  overflow: hidden;
}
<html>
  <head>
  </head>
  <body>
    <section>
      <header>
        <div class="col">Column A</div>
        <div class="col">Column B</div>
        <div class="col">Column C</div>
      </header>
      <div class="line-break"></div>
      <div class="row">
        <div class="col">1</div>
        <div class="col">2</div>
        <div class="col">3</div>
      </div>
    </section>
  </body>
</html>

Upvotes: 63

Views: 235377

Answers (5)

Jo Liss
Jo Liss

Reputation: 32927

I recommend using CSS Grid, not Flexbox. Here's why:

CSS Grid has mostly the same features as Flexbox, but is made for exactly the use case of tabular alignment.

Flexbox is made for aligning in one dimension, thus leaving the columns unaligned (or the rows, depending on flex-direction), unless you resort to assigning fixed column widths. Here is an example showing the behavior of Flexbox:

Flexbox example

CSS Grid, on the other hand, is made for aligning in both dimensions, which is what you want for tabular data:

Grid example

An important feature not shown in this diagram: In a CSS Grid, by setting the column width to auto, you can allow the contents of any cell to dynamically expand the width of its entire column -- just like with a <table>. You can't do this with Flexbox, at least not in both dimensions.

The diagrams above are from the MDN article Relationship of grid layout to other layout methods, which goes into this topic in much greater detail.

Upvotes: 3

Asons
Asons

Reputation: 87191

If the content you are going to present is of type tabular data, then a table is the proper way.

HTML 5.1 W3C Recommendation, 1 November 2016, 4.9 Tabular data

Given that you can't, or don't want to, alter the markup, this can be done using CSS Table, and with that easily swap between any display type such as flex, block, etc., or even float, using media query etc.

I also removed the <div class="line-break"></div> element, since you don't need, though if it is rendered by a component or similar, leaving it as is won't cause any problem.

Using CSS Table

section {
  display: table;
  width: 100%;
}

section > * {
  display: table-row;
}

section .col {
  display: table-cell;
}
<html>
  <head>
  </head>
  <body>
    <section>
      <header>
        <div class="col">Column A</div>
        <div class="col">Column B</div>
        <div class="col">Column C</div>
      </header>
      <div class="row">
        <div class="col">1</div>
        <div class="col">2</div>
        <div class="col">3</div>
      </div>
    </section>
  </body>
</html>


If you still need, or have to, use Flexbox, this answer of mine mention the difference between CSS Table and Flexbox on two important features:


Updated, a sample showing some useful Flexbox stuff, with varying width's and span columns.

Using Flexbox

.tbl {
  display: flex;
  flex-direction: column;
}
.row {
  display: flex;
  min-height: 50px;
}
.cell {
  flex: 4;
  border: 1px solid red;
}
.cell:nth-child(1) {
  flex: 1;
}
.cell:nth-child(2) {
  flex: 2;
}
.cell.span4-5 {
  flex: 8 24px;                    /*  col 4,5 flex-grow/border/padding  */
}
.cell.span3-4 {
  flex: 8 24px;                    /*  col 3,4 flex-grow/border/padding  */
}
.cell.span3-5 {
  flex: 12 36px;                   /*  col 3,4,5 flex-grow/border/padding  */
}
.row:first-child .cell {
  display: flex;
  justify-content: center;         /*  center horiz. */
  align-items: center;             /*  center vert. */
}
.row .cell {
  padding: 5px;
  box-sizing: border-box;
}
<div class="tbl">
  <div class="row">
    <div class="cell">ID </div>
    <div class="cell">Nr </div>
    <div class="cell">Header 1 </div>
    <div class="cell span4-5"> Header 2 </div>
  </div>
  <div class="row">
    <div class="cell">1</div>
    <div class="cell">2</div>
    <div class="cell">Content</div>
    <div class="cell">Content</div>
    <div class="cell">Content</div>
  </div>
  <div class="row">
    <div class="cell">2</div>
    <div class="cell">3</div>
    <div class="cell span3-5">Content</div>
  </div>
  <div class="row">
    <div class="cell">1</div>
    <div class="cell">2</div>
    <div class="cell span3-4">Content</div>
    <div class="cell">Content</div>
  </div>
</div>

Upvotes: 51

angry kiwi
angry kiwi

Reputation: 11435

Use CSS Grid. You can style any table the way you like.

Keep in mind If your table is more than 700 rows, the fram rate will start to drop, no matter what js framework you use. react, angular, vue or vanila JS. the scrolling will get real laggy.

And the maximum you row can use is 1000. More than that the extra row will create bad graphic. But you wont reach 1000 anyway, because at 700th row, the scrolling speed, starts to get bad.

If somehow you need to display more than 1000 rows, you will visualized lib. Every js framework has a lib to do so. Basically, it will render the rows in the view port. The rows that not in the view port will not be rendered. They will only be rendered when user scrolls.

This is year 2021, chances you read this answer in the future, the browsers vendor might probably fix the performance of 1000 rows, they might even extend that limit. So try it out.

Upvotes: 2

WebDev-SysAdmin
WebDev-SysAdmin

Reputation: 369

This code works for me:

        * {
            box-sizing: border-box;
        }


        body, html {
            height: 100%;
            margin: 0;
        }


        #container {
            width: 400px;
            display: flex;
            flex-direction: column;
            justify-content: flex-start;
            align-items: flex-start;
            background-color: lightgrey;
            padding: 10px;

        }


        .shelf {

            flex: 1 1 auto;
            width: 100%;
            margin-bottom: 10px;
            border: 1px solid black;
            background-color: lightgreen;

            display: flex;
            flex-direction: row;

        }
        .shelf:last-child {
            margin-bottom: 0;
        }


        .labelbox {
            flex: 0 0 35%;
        }


        .valuebox {
            flex: 0 0 65%;
        }
<div id="container">

    <div class="shelf">
        <div class="labelbox">Name: </div> <div class="valuebox">Barry Carter</div>
    </div>

    <div class="shelf">
        <div class="labelbox">DOB:</div><div class="valuebox">10/12/1980</div>
    </div>

    <div class="shelf">

        <div class="labelbox">
            Description:
        </div>

        <div class="valuebox">

            This content goes on and on and will force the height to expand. And the label box to the left will
            "move" with it. There need not be much of a relation other than that their parent div/flex-container is
            getting taller as well.

        </div>

    </div>

    <div class="shelf">
        <div class="labelbox">Group:</div><div class="valuebox">Advanced</div>
    </div>

    <div class="shelf">
        <div class="labelbox">End Date:</div><div class="valuebox">2020-09-20</div>
    </div>

</div>

Upvotes: 8

Michael Benjamin
Michael Benjamin

Reputation: 371003

header, .row {
  display: flex;  /* aligns all child elements (flex items) in a row */
}

.col {
  flex: 1;        /* distributes space on the line equally among items */
}
<section>
  <header>
    <div class="col">Column A</div>
    <div class="col">Column B</div>
    <div class="col">Column C</div>
  </header>
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
  </div>
</section>

Upvotes: 61

Related Questions