Layout a flex box similar to a table? - html

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>

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>

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:
Can flexbox handle varying sizes of columns but consistent row height?
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>

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>

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.

Related

Need helping creating a Flex-box container that wraps and is even

Trying to create a 2 column, 3 row flexbox container for a food menu. The product elements (which should be 2 per row) do not wrap when shrunk. I'm looking for a way to create a wrapping layout using flexbox. Also, what would be the best way to use Media Queries for the product title to be displayed when the layout is shrunk for mobile-size?
I'm attaching my jsfiddle code:
https://jsfiddle.net/5ksd34nf/#&togetherjs=Ix1LEBTca6
(keep in mind without the images, the design changes so I'm attaching photos)
The HTML is:
`
<section class="menu-page" id="Menu">
<div class="TitleWrapper">
<h1 class="title">Menu</h1>
</div>
<div class="menu-list">
<div class="product">
<div class="imgwrapper">
<img src="images/burger.jpg" alt="Burger" class="food-image">
</div>
<div class="text">
<div class="product-content">
<h3 class="name">Burgers</h3>
<h3 class="price">10 €</h3>
</div>
<div class="ptags">
<p class="allergens">Allergens:</p>
<p class="info">Milk, Gluten</p>
</div>
</div>
</div>
How can I space out the Price from the Name using flexbox?
Basically, you can do something like this:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.flex-container {
display: flex;
flex-wrap: wrap;
}
.flex-item {
border: 1px solid gray;
width: 100%;
margin-bottom: 20px;
}
.flex-sub-item {
display: flex;
justify-content: space-between;
}
#media screen and (min-width: 400px) {
.flex-item {
width: calc((100% - 20px) / 2);
margin-right: 20px;
}
.flex-item:nth-child(2n) {
margin-right: 0;
}
}
<div class="flex-container">
<div class="flex-item">
<div class="flex-sub-item">
<div>Title</div>
<div>Price</div>
</div>
</div>
<div class="flex-item">
<div class="flex-sub-item">
<div>Title</div>
<div>Price</div>
</div>
</div>
</div>

How to make div rows fill available height

I have a simple web page that contains multiple rows. I want to be able to have those rows fill the available space equally (with a minimum height as well). The number of rows is also dynamic.
Here is the code:
.outer {
display: block;
overflow: auto;
height: 100%;
}
.row {
border: 1px solid blue;
background-color: red;
min-height: 120px;
}
<div class="outer">
<div class="row">
test 1
</div>
<div class="row">
test 2
</div>
<div class="row">
test 3
</div>
<div class="row">
test 4
</div>
<div class="row">
test 5
</div>
</div>
The colouring is just there so we can see what is happening.
Any help would be appreciated. The other questions on SO are not helping my specific case. Or at least I can't find one that does :)
.outer {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.row {
flex: 1;
}

Is there a DRY way of Flex ordering?

Newbie to flex use/web development.
I currently have 6 boxes within a flex container ordered like this for mobile devices:
Mobile view
with code more or less like this (CSS not included, but the class “box” is the grey box you see above):
<div class="flex-container">
<div class=“box item” />
<div class=“text item”>
<h4>Text</h4>
</div>
<div class=“box item” />
<div class=“text item”>
<h4>Text</h4>
</div>
<div class=“box item” />
<div class=“text item”>
<h4>Text</h4>
</div>
</div>
This is what I want for mobile devices!
For desktop however, I’d like to achieve this: Desktop View
Currently, the only way I’m achieving this is by using this unappealing flex order css:
item:nth-of-type(1) {order:1;}
item:nth-of-type(2) {order:2;}
item:nth-of-type(3) {order:4;}
item:nth-of-type(4) {order:3;}
item:nth-of-type(5) {order:5;}
item:nth-of-type(6) {order:6;}
My question is, is there a way to achieve my desired goal (i.e. switching the order of items 3 and 4) without having to order every single item in the container creating a yucky, repetitive block of code?
You can accomplish that with only 2 CSS selectors, and with order defaults to 0, we re-position item 3 and 5/6, here done with a media query for screens wider than 600px, to 1 and 2.
.item:nth-of-type(3) { order:1; } /* put 3 after 4 */
.item:nth-of-type(n+5) { order:2; } /* put 5,6 after 3 */
Stack snippet
.flex-container { display: flex; flex-wrap: wrap; }
.item { height: 50px; flex-basis: 100%; }
.box { background: lightgray; }
#media (min-width: 600px) {
.item { flex-basis: 50%; }
.item:nth-of-type(3) { order:1; } /* put 3 after 4 */
.item:nth-of-type(n+5) { order:2; } /* put 5,6 after 3 */
}
<div class="flex-container">
<div class="box item"></div>
<div class="text item">
<h4>Text</h4>
</div>
<div class="box item"></div>
<div class="text item">
<h4>Text</h4>
</div>
<div class="box item"></div>
<div class="text item">
<h4>Text</h4>
</div>
</div>
As far as I know if you reorder items, you need to explicitly order items after the re-ordered items. So you could probably do:
item:nth-of-type(3) {order:4;}
item:nth-of-type(4) {order:3;}
item:nth-of-type(5) {order:5;}
item:nth-of-type(6) {order:6;}
If you only ever want to switch those two, you could wrap them in another flex container and simply switch ordering within. That way your outer flow won't have to be redefined, and you could set up the container to be reused in other areas where you need to achieve the same thing.
<div class="container">
<div class="item one">One</div>
<div class="item two">Two</div>
<div class="item three">Three</div>
<div class="switch">
<div class="item four">Four</div>
<div class="item five">Five</div>
</div>
<div class="item six">Six</div>
<div class="item seven">Seven</div>
</div>
.item {
flex: 1 0 100%;
line-height: 39px;
height: 40px;
width: 100%;
background: #cecece;
margin-bottom: 10px;
text-align: center;
}
.container {
display: flex;
flex-wrap: wrap;
}
.switch {
display: flex;
flex-wrap: wrap;
flex: 1 0 100%;
}
#media (min-width: 400px) {
.switch .item:nth-of-type(1) {
order: 2;
}
}
fiddle
In addition to TripWire's answer, you need not to set a different order number for every <div> after the 4th one.
.item:nth-of-type(3), .item:nth-of-type(4) ~ .item {order:100;}
.item:nth-of-type(4) {order:50;}
A pen: https://codepen.io/israfel/pen/eEbWWG

Make some items twice the height in a flexbox grid

I'm trying to create a simple flexbox grid with two columns, however with the option of declaring one of the children as "featured" making it twice the height of the normal children, so given the following markup:
<div class="container">
<div class="child featured">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
</div>
You'd end up with something like this (margins/padding/border for illustrative purposes only):
However I can't seem to get it to work, the children all just stack under the featured child rather than fill the available space.
My basic CSS is:
.container {
display: flex;
flex-flow: column wrap;
}
.child {
flex: 1 0 50%;
height: 50vh;
max-width: 50%;
}
.child.featured {
height: 100vh;
}
Any idea what I'm doing wrong, or if there is a better approach to this (without resorting to JavaScript)?
Flexbox doesn't support such grid. But you can do it using floats:
.child {
float: left;
height: 50vh;
width: 50%;
outline: 1px solid red;
}
.child.featured {
height: 100vh;
}
<div class="container">
<div class="child featured">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
</div>
Any idea what I'm doing wrong, or if there is a better approach to this (without resorting to JavaScript)?
The problem is that flexbox is not designed to create anything more than simple grids. Once you ask for something like a masonry layout (which is what you're after), you'll need hacks and workarounds for flexbox to complete the task.
However, this layout can be achieved easily in CSS Grid:
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.child.featured {
grid-row-end: span 2;
}
/* non-essential decorative styles */
.container {
padding: 10px;
border: 2px solid gray;
background-color: lightgray;
height: 50vh;
}
.child {
background-color: deepskyblue;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3em;
}
<div class="container">
<div class="child featured">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
</div>
jsFiddle demo
For a complete explanation of the problem when using flexbox, and how the Grid functions work, see this post:
CSS-only masonry layout but with elements ordered horizontally

How to "skip" div in HTML tree when using Flexbox?

I am trying to use Flexbox on an existing site, and I need to somehow tell the browser to "skip" several divs in the tree.
Let me explain on a simplified example. I have a HTML like this
<body style="display: flex;
min-height: 100vh;
flex-direction: column;">
<div id="want_to_skip">
<div style="flex: 1;">
</div>
<div id="footer"></div>
</div>
</body>
and I want it to behave as it was like this
<body style="display: flex;
min-height: 100vh;
flex-direction: column;">
<div style="flex: 1;">
</div>
<div id="footer"></div>
</body>
Unfortunately, I need the "skipped" div to be there and cannot remove it. What should I do?
CSS Display L3 introduces display: contents:
The element itself does not generate any boxes, but its children and
pseudo-elements still generate boxes as normal. For the purposes of
box generation and layout, the element must be treated as if it had
been replaced with its children and pseudo-elements in the document
tree.
#want_to_skip {
display: contents;
}
body {
display: flex;
margin: 0;
}
body > div {
flex: 1;
}
.wrapper {
display: flex;
min-height: 100vh;
flex-direction: column;
border: 1px solid;
}
.top {
flex: 1;
}
.want_to_skip {
display: contents;
}
<div>
<div class="wrapper">
<div>
<div class="top"> No skipping - Top </div>
<div class="bottom">No skipping - Bottom</div>
</div>
</div>
</div>
<div>
<div class="wrapper">
<div class="want_to_skip">
<div class="top"> Skipping - Top </div>
<div class="bottom">Skipping - Bottom</div>
</div>
</div>
</div>
<div>
<div class="wrapper">
<div class="top"> Desired - Top </div>
<div class="bottom">Desired - Bottom</div>
</div>
</div>
Currently, it is only supported by Firefox 37.