CSS Nth Child calculate top height based on N - css

I am trying to use CSS to calculate the top height to add every 5+1 elements.
The following code moves a series of absolute positioned elements to their respective places.
.screen [data-app]:nth-child(5n-4) { left:0%; }
.screen [data-app]:nth-child(5n-3) { left:20%; }
.screen [data-app]:nth-child(5n-2) { left:40%; }
.screen [data-app]:nth-child(5n-1) { left:60%; }
.screen [data-app]:nth-child(5n) { left:80%; }
This creates the illusion of five columns with absolutely positioned elements. Now what I'd like to do is for the next row of five to also have top:180px added to them, and the row after that top:360px etc.. etc..
Can this be done without the need to write CSS code for the position of every single element. Some way of applying a top attribute for each group of five based on n value of the current element.

You can either use SASS or Flexbox in order to achieve the result you're looking for. In this case SASS will create a more bloated CSS-file than ideal, but will use the rules you posit, while Flexbox will be more future-proof and easily maintained.
The HTML
<div class="screen">
<div data-app>asdf1</div>
<div data-app>asdf2</div>
<div data-app>asdf3</div>
<div data-app>asdf4</div>
<div data-app>asdf5</div>
<div data-app>asdf6</div>
<div data-app>asdf7</div>
<div data-app>asdf8</div>
<div data-app>asdf9</div>
<div data-app>asdf10</div>
<div data-app>asdf11</div>
</div>
SASS
.screen {
position: relative;
}
.screen [data-app] {
$height: 180px;
$offset: 20%;
$blocks_per_row: 5;
position: absolute;
width: 20%;
#for $i from 0 through 20 {
$y: floor($i / $blocks_per_row);
$x: $i % 5;
&:nth-child(#{$y}n+#{$i}) {
left: $x * $offset;
top: $y * $height;
}
}
}
Flexbox
.screen {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-content: flex-start;
align-items: flex-start;
}
.screen [data-app] {
flex: 0 1 20%;
height: 180px;
}
As you can see, there's no upper limit in the Flexbox solution and it's very clean. I hope any of these solutions help you.

Related

Scale font to size when text exceeds container?

I want font size to be 54px if the text fits inside the container, otherwise it should be 36px.
I was considering whether I can achieve this with a pure CSS solution, using the scale function to collapse to either of the two. If the container can be assumed to be full with, I guess I could use vw as a base for a calculation?
But I am very much stuck on this. Could anyone give me a hint, as to how I can achieve this or something close to it.
If you would like to put some text inside a container and have it size itself to fill that container, then CSS Tricks has an article on Fitting Text to a Container that will cover your options.
You could also use Viewport Sized Typography which take advantage of viewport units such as:
1vw = 1% of viewport width
1vh = 1% of viewport height
1vmin = 1vw or 1vh, whichever is smaller
1vmax = 1vw or 1vh, whichever is larger
One unit of any v* is 1% of the viewport axis. Where the “Viewport” == browser window size == window object. If the viewport is 50cm wide, 1vw == 0.5cm. Using viewport units alone such as font-size: 4vw; can make the text appear too big or too small when varying the window width and bring accessibility issues (as the user preferences are not taken into account).
Lastly, you could use clamp() to achieve Simplified Fluid Typography. Clamp takes three values, a min, max, and a flexible unit in the middle that it will use in case the value is between the min and max.
If you want the font size to be a minimum of 36px and maximum 54px, you could use clamp() like this and vary the "flexible unit" to your liking. Here is an example of fluid typography for an <h1> element inside a container.
body {
font-size: 1rem;
margin: 0 auto;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.container {
border: .2rem solid #f06;
padding: .5rem;
height: 100%;
width: auto;
}
.container h1 {
font-size: 36px; /* fallback */
font-size: clamp(36px, 10vw, 54px);
}
<body>
<div class="container">
<h1>Heading text</h1>
</div>
</body>
Browser support for clamp() is pretty good, but you’d probably want to put a font-size declaration before it to set an acceptable fallback value.
In conclusion, if you needed to set an explicit width and height for said container, you might want to use media queries along with viewport units, calc(), or clamp() depending on the size of the content box in which the text resides.
i think it's nearly impossible to calculate it without js.
below is an code example how i would do it in jquery or you could use the following jquery plugin, but i never tested this plugin before: FitText.js
for (let container of $('.text-container')){
container = $(container);
let textInner = $(container).find('.text-inner');
console.log(container.width());
console.log(textInner.width());
if (container.width()<textInner.width()){
textInner.addClass('fs-36');
textInner.removeClass('fs-54');
} else {
textInner.addClass('fs-54');
textInner.removeClass('fs-36');
}
}
.text-container,
.text-container-before{
position: relative;
width: 250px;
height: 60px;
background: #333;
color: #090;
overflow: visible;
margin: 20px 0;
}
.text-inner,
.text-before{
position: absolute;
top: 0;
left: 0;
height: 100%;
background: #0399;
word-break: keep-all;
white-space: nowrap;
}
.fs-54{
font-size: 54px;
}
.fs-36{
font-size: 36px;
}
/* just added because console window is hiding running code snipped */
.spacer{
height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>before</h1>
<div class="text-container-before">
<div class="text-before fs-54">a bit longer text</div>
</div>
<div class="text-container-before">
<div class="text-before fs-54">shorter text</div>
</div>
<h1>after</h1>
<div class="text-container">
<div class="text-inner fs-54">a bit longer text</div>
</div>
<div class="text-container">
<div class="text-inner fs-54">shorter text</div>
</div>
<div class="spacer"></div>
This question awsers was not exactly what I was looking for, so here is a solution to scale down the text if it would exceed the size of the parent container, by making it smaller until it fits the container.
for(const element of document.getElementsByClassName("shrink"))
{
var size = parseInt(getComputedStyle(element).getPropertyValue('font-size'));
const parent_width = parseInt(getComputedStyle(element.parentElement).getPropertyValue('width'))
while(element.offsetWidth > parent_width)
{
element.style.fontSize = size + "px"
size -= 1
}
}
.container{
border:1px dashed #ccc;
display:flex;
align-items:center;
justify-content:center;
width:200px;
height:50px;
margin:10px;
}
.shrink{
white-space: nowrap;
font-size:80px;
}
<div class="container"><span class="shrink">long text to shrink</span></div>
<div class="container"><span class="shrink">small text</span></div>
try word-break: break-all;, I think this will solve your problem
Not, Sure about CSS solution. But, My JS solution can resolve your problem.
Refer below code. Class class-54 and class-36 will change based on content:
// creating node
function createNode(element) {
return document.createElement(element);
}
// creating append
function append(parent, el) {
return parent.appendChild(el);
}
var getPageTitle = document.getElementById("pageTitle"),
textLengthWidth = 0;
const getPageTitleText = getPageTitle.textContent;
getPageTitle.innerHTML = "";
for (let index = 0; index < getPageTitleText.length; index++) {
const span = createNode("span");
span.innerHTML = getPageTitleText.charAt(index);
append(getPageTitle, span);
if (index == getPageTitleText.length - 1) {
calcFontSize();
}
}
function calcFontSize() {
var listOfSpan = document.querySelectorAll("#pageTitle span");
listOfSpan.forEach(element => {
textLengthWidth += element.offsetWidth;
});
console.log("total DIV width: " + getPageTitle.offsetWidth);
console.log("total Content width: " + textLengthWidth);
getPageTitle.offsetWidth < textLengthWidth ? getPageTitle.classList.add("class-36") : getPageTitle.classList.add("class-54");
}
* {
padding: 0;
margin: 0;
font-size: 14px;
}
.title {
display: block;
width: 100vw;
}
h1, h1 span {
font-size: 54px;
}
.class-54 span {
font-size: 54px;
}
.class-36 span {
font-size: 36px;
}
<div class="title">
<h1 id="pageTitle">Hello World! Hello World!</h1>
</div>
Try this
.thingy {
font-size:54px;
overflow-wrap: break-word;
}
<h1 class="thingy">Hel000000000000000000ooo</h1>

Trying to create a row with 4 cells

I am trying to create a row with 4 cells and I don't know why its not working.
I have created a parent row and 4 children.
<div className='row'>
<div className='col-1-of-4'>
hi
</div>
<div className='col-1-of-4'>
hi
</div>
<div className='col-1-of-4'>
hi
</div>
<div className='col-1-of-4'>
hi
</div>
</div>
(Ignore the className for class, as I am using react)
and the css properties are:
[class^="col-"] {
float: left;
&:not(:last-child) {
margin-right: $gutter-horizontal;
}
}
.col-1-of-4 {
width: calc((100% - #{$gutter-horizontal}) / 4);
}
What it does is, calculates total width, and then subtracts the margin and then divides by 4.
Technically it should work and I should be able to see 4 cells in a row.
But the result I get is, 3 cells in a row and the fourth one on the next line.
the result should be something like this
hi hi hi hi
but the actual result is
hi hi hi
hi
Here is the working code
https://codepen.io/sarmad1995/pen/REYXBV?editors=1100
You shouldn't divide the margin inside your calculation. It should be outside or will you will remove less than the margin set for each element. You are setting X margin and only removing X/4 so each element will take 25% - X/4 + X (the last one 25% - X/4) as a space thus the total will be 100% + 2X which bigger than 100%.
.col-1-of-4 {
width: calc(100% / 4 - #{$gutter-horizontal});
}
.row {
max-width: 114rem;
margin: 0 auto;
}
.row:not(:last-child) {
margin-bottom: 8rem;
}
.row::after {
content: "";
display: table;
clear: both;
}
.row [class^="col-"] {
float: left;
}
.row [class^="col-"]:not(:last-child) {
margin-right: 6rem;
}
.row .col-1-of-4 {
width: calc(100% / 4 - 6rem);
background-color: red;
}
<div class='row'>
<div class='col-1-of-4'>
hi
</div>
<div class='col-1-of-4'>
hi
</div>
<div class='col-1-of-4'>
hi
</div>
<div class='col-1-of-4'>
hi
</div>
</div>
And in case you need a space between behavior (which is what you want) you can do like this:
.col-1-of-4 {
width: calc(100% / 4 - 3*#{$gutter-horizontal}/4);
}
That you can also write like this:
.col-1-of-4 {
width: calc((100% - 3*#{$gutter-horizontal})/4);
}
You need to remove the 3 margins (defined for the first 3 elements) from the total width then divide by 4:
.row {
max-width: 114rem;
margin: 0 auto;
}
.row:not(:last-child) {
margin-bottom: 8rem;
}
.row::after {
content: "";
display: table;
clear: both;
}
.row [class^="col-"] {
float: left;
}
.row [class^="col-"]:not(:last-child) {
margin-right: 6rem;
}
.row .col-1-of-4 {
width: calc(100% / 4 - 3*6rem/4);
background-color: red;
}
<div class='row'>
<div class='col-1-of-4'>
hi
</div>
<div class='col-1-of-4'>
hi
</div>
<div class='col-1-of-4'>
hi
</div>
<div class='col-1-of-4'>
hi
</div>
</div>
You should apply the same logic for all your other classes
Looking at your codepen example, you are setting a margin-right.
[class^="col-"] {
float: left;
&:not(:last-child) {
margin-right: $gutter-horizontal;
}
}
Removing this gives you the four columns as described.
Please in future include all relevant code to your question.

Horizontal center for flex items [duplicate]

I want the flex items to be centered but when we have a second line, to have 5 (from image below) under 1 and not centered in the parent.
Here's an example of what I have:
ul {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin: 0;
padding: 0;
}
li {
list-style-type: none;
border: 1px solid gray;
margin: 15px;
padding: 5px;
width: 200px;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
http://jsfiddle.net/8jqbjese/2/
Flexbox Challenge & Limitation
The challenge is to center a group of flex items and left-align them on wrap. But unless there is a fixed number of boxes per row, and each box is fixed-width, this is currently not possible with flexbox.
Using the code posted in the question, we could create a new flex container that wraps the current flex container (ul), which would allow us to center the ul with justify-content: center.
Then the flex items of the ul could be left-aligned with justify-content: flex-start.
#container {
display: flex;
justify-content: center;
}
ul {
display: flex;
justify-content: flex-start;
}
This creates a centered group of left-aligned flex items.
The problem with this method is that at certain screen sizes there will be a gap on the right of the ul, making it no longer appear centered.
This happens because in flex layout (and, actually, CSS in general) the container:
doesn't know when an element wraps;
doesn't know that a previously occupied space is now empty, and
doesn't recalculate its width to shrink-wrap the narrower layout.
The maximum length of the whitespace on the right is the length of the flex item that the container was expecting to be there.
In the following demo, by re-sizing the window horizontally, you can see the whitespace come and go.
DEMO
A More Practical Approach
The desired layout can be achieved without flexbox using inline-block and media queries.
HTML
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
CSS
ul {
margin: 0 auto; /* center container */
width: 1200px;
padding-left: 0; /* remove list padding */
font-size: 0; /* remove inline-block white space;
see https://stackoverflow.com/a/32801275/3597276 */
}
li {
display: inline-block;
font-size: 18px; /* restore font size removed in container */
list-style-type: none;
width: 150px;
height: 50px;
line-height: 50px;
margin: 15px 25px;
box-sizing: border-box;
text-align: center;
}
#media screen and (max-width: 430px) { ul { width: 200px; } }
#media screen and (min-width: 431px) and (max-width: 630px) { ul { width: 400px; } }
#media screen and (min-width: 631px) and (max-width: 830px) { ul { width:600px; } }
#media screen and (min-width: 831px) and (max-width: 1030px) { ul { width: 800px; } }
#media screen and (min-width: 1031px) and (max-width: 1230px) { ul { width: 1000px; } }
The above code renders a horizontally-centered container with left-aligned child elements like this:
DEMO
Other Options
Properly sizing and aligning the flex item(s) on the last row
Desandro Masonry
Masonry is a JavaScript grid layout library. It
works by placing elements in optimal position based on available
vertical space, sort of like a mason fitting stones in a wall. You’ve
probably seen it in use all over the Internet.
source: http://masonry.desandro.com/
CSS Grid Layout Module Level 1
This CSS module defines a two-dimensional grid-based layout system, optimized for user interface design. In the grid layout model, the children of a grid container can be positioned into arbitrary slots in a predefined flexible or fixed-size layout grid.
source: https://drafts.csswg.org/css-grid/
You can achieve it with CSS Grid, just use repeat(autofit, minmax(width-of-the-element, max-content))
ul {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
grid-gap: 16px;
justify-content: center;
padding: initial;
}
li {
list-style-type: none;
border: 1px solid gray;
padding: 5px;
width: 210px;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
</ul>
http://jsfiddle.net/rwa20jkh/
Somehow, #Joe82 answer did not work for me. However, I found it to be the right approach. After reading this article about auto-fit and auto-fill I found out that auto-fit creates new columns when possible; however, it collapses them, so that the grid-items fill out the whole available space, if their max-width allows them this.
For those interested: auto-fill also creates new columns when possible, but does not let them collapse, so it creates empty visible columns, which will take up space.
You can see this in the following image:
Because of this, I used repeat(auto-fit, minmax(10rem, 1fr) for `grid-template-columns.
Then I set justify-items to center, this aligns the items inside their grid areas on the inline axis.
I also wanted some "margins" between the columns and rows, so I added a row-gap and a column-gap of 1rem with the shorthand.
As a result I added the following CSS to my div with the grid items inside it:
.card-section {
width: 100%;
display: grid;
justify-items: center;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
}
I know this is not exactly what OP wanted to achieve, but maybe it helps someone, who has the same problem as me and stumbles upon this question.
You can place invisible elements with the same class as the others (removed on example for exibition purposes) and height set to 0. With that, you will be able to justify the items to the very start of the grid.
Example
<div class="table-container">
<div class="table-content">
<p class="table-title">Table 1</p>
<p class="mesa-price">$ 20</p>
</div>
<!-- Make stuff justified start -->
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
</div>
Result
As #michael suggested, this is a limitation with current flexbox. But if you want to still use flex and justify-content: center;, then we can workaround this by adding a dummy li element and assign margin-left.
const handleResize = () => {
const item_box = document.getElementById('parentId')
const list_length = item_box.clientWidth
const product_card_length = 200 // length of your child element
const item_in_a_row = Math.round(list_length/product_card_length)
const to_be_added = item_in_a_row - parseInt(listObject.length % item_in_a_row) // listObject is the total number items
const left_to_set = (to_be_added - 1 ) * product_card_length // -1 : dummy item has width set, so exclude it when calculating the left margin
const dummy_product = document.querySelectorAll('.product-card.dummy')[0]
dummy_product.style.marginLeft = `${left_to_set}px`
}
handleResize() // Call it first time component mount
window.addEventListener("resize", handleResize);
Check this fiddle (resize and see ) or video for reference
One way to get the desired style with margins is to do the following:
#container {
display: flex;
justify-content: center;
}
#innercontainer {
display: flex;
flex: 0.9; -> add desired % of margin
justify-content: flex-start;
}
I ran into this problem while coding with React Native. There's an elegant solution that you can have using FlexBox. In my particular situation, I was trying to center three flex boxes (Flex: 2) inside another using alignItems. The solution I came up with was using two empty s, each with Flex: 1.
<View style={{alignItems: 'center', flexWrap: 'wrap', flexDirection: 'row'}}>
<View style={{flex: 1}} />
// Content here
<View style={{flex: 1}} />
</View>
Easy enough to convert to web / CSS.
The easiest way I've found to fix this is just simply add some place holders with visibility: hidden. That way it maintains the correct spacing as it wraps.
TL;DR
You can stuff some filler elements to the end of your container, and set visibility: hidden to make it invisible, and remember to set height: 0px to prevent the height be taken.
Demo
In the example below, you can click the button to watch the changes.
const container = document.getElementsByClassName('container')[0];
const item = document.createElement('div');
item.classList.add("item");
const filler = document.createElement('div')
filler.classList.add("filler");
item.appendChild(filler);
Array.from(Array(5).keys()).forEach(() => {
container.appendChild(item.cloneNode(true));
});
function onShowClick() {
const filler = document.getElementsByClassName('filler')
for (let i = 0; i < filler.length; i++) {
filler[i].style.border = "1px dashed #686868"
filler[i].style.visibility = "visible"
filler[i].style.height = "100px"
}
};
function onHideClick() {
const filler = document.getElementsByClassName('filler')
for (let i = 0; i < filler.length; i++) {
filler[i].style.border = "none"
filler[i].style.visibility = "hidden"
filler[i].style.height = "0px"
}
};
* {
box-sizing: border-box;
}
#root {
height: 400px;
width: 500px;
border: 1px solid #ff955a;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item {
padding: 5px;
}
.content-box {
height: 100px;
width: 100px;
padding: 10px;
background-color: lightblue;
border-radius: 10px;
}
.filler {
visibility: hidden;
height: 0px;
width: 100px;
}
<div id="root">
<button onclick="onShowClick()">Show</button>
<button onclick="onHideClick()">Hide</button>
<div class="container">
<div class="item">
<div class="content-box">1</div>
</div>
<div class="item">
<div class="content-box">2</div>
</div>
<div class="item">
<div class="content-box">3</div>
</div>
<div class="item">
<div class="content-box">4</div>
</div>
<div class="item">
<div class="content-box">5</div>
</div>
<div class="item">
<div class="content-box">6</div>
</div>
<div class="item">
<div class="content-box">7</div>
</div>
<div class="item">
<div class="content-box">8</div>
</div>
<div class="item">
<div class="content-box">9</div>
</div>
</div>
</div>

Flexbox quirks, centering of grid [duplicate]

I want the flex items to be centered but when we have a second line, to have 5 (from image below) under 1 and not centered in the parent.
Here's an example of what I have:
ul {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin: 0;
padding: 0;
}
li {
list-style-type: none;
border: 1px solid gray;
margin: 15px;
padding: 5px;
width: 200px;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
http://jsfiddle.net/8jqbjese/2/
Flexbox Challenge & Limitation
The challenge is to center a group of flex items and left-align them on wrap. But unless there is a fixed number of boxes per row, and each box is fixed-width, this is currently not possible with flexbox.
Using the code posted in the question, we could create a new flex container that wraps the current flex container (ul), which would allow us to center the ul with justify-content: center.
Then the flex items of the ul could be left-aligned with justify-content: flex-start.
#container {
display: flex;
justify-content: center;
}
ul {
display: flex;
justify-content: flex-start;
}
This creates a centered group of left-aligned flex items.
The problem with this method is that at certain screen sizes there will be a gap on the right of the ul, making it no longer appear centered.
This happens because in flex layout (and, actually, CSS in general) the container:
doesn't know when an element wraps;
doesn't know that a previously occupied space is now empty, and
doesn't recalculate its width to shrink-wrap the narrower layout.
The maximum length of the whitespace on the right is the length of the flex item that the container was expecting to be there.
In the following demo, by re-sizing the window horizontally, you can see the whitespace come and go.
DEMO
A More Practical Approach
The desired layout can be achieved without flexbox using inline-block and media queries.
HTML
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
CSS
ul {
margin: 0 auto; /* center container */
width: 1200px;
padding-left: 0; /* remove list padding */
font-size: 0; /* remove inline-block white space;
see https://stackoverflow.com/a/32801275/3597276 */
}
li {
display: inline-block;
font-size: 18px; /* restore font size removed in container */
list-style-type: none;
width: 150px;
height: 50px;
line-height: 50px;
margin: 15px 25px;
box-sizing: border-box;
text-align: center;
}
#media screen and (max-width: 430px) { ul { width: 200px; } }
#media screen and (min-width: 431px) and (max-width: 630px) { ul { width: 400px; } }
#media screen and (min-width: 631px) and (max-width: 830px) { ul { width:600px; } }
#media screen and (min-width: 831px) and (max-width: 1030px) { ul { width: 800px; } }
#media screen and (min-width: 1031px) and (max-width: 1230px) { ul { width: 1000px; } }
The above code renders a horizontally-centered container with left-aligned child elements like this:
DEMO
Other Options
Properly sizing and aligning the flex item(s) on the last row
Desandro Masonry
Masonry is a JavaScript grid layout library. It
works by placing elements in optimal position based on available
vertical space, sort of like a mason fitting stones in a wall. You’ve
probably seen it in use all over the Internet.
source: http://masonry.desandro.com/
CSS Grid Layout Module Level 1
This CSS module defines a two-dimensional grid-based layout system, optimized for user interface design. In the grid layout model, the children of a grid container can be positioned into arbitrary slots in a predefined flexible or fixed-size layout grid.
source: https://drafts.csswg.org/css-grid/
You can achieve it with CSS Grid, just use repeat(autofit, minmax(width-of-the-element, max-content))
ul {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
grid-gap: 16px;
justify-content: center;
padding: initial;
}
li {
list-style-type: none;
border: 1px solid gray;
padding: 5px;
width: 210px;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
</ul>
http://jsfiddle.net/rwa20jkh/
Somehow, #Joe82 answer did not work for me. However, I found it to be the right approach. After reading this article about auto-fit and auto-fill I found out that auto-fit creates new columns when possible; however, it collapses them, so that the grid-items fill out the whole available space, if their max-width allows them this.
For those interested: auto-fill also creates new columns when possible, but does not let them collapse, so it creates empty visible columns, which will take up space.
You can see this in the following image:
Because of this, I used repeat(auto-fit, minmax(10rem, 1fr) for `grid-template-columns.
Then I set justify-items to center, this aligns the items inside their grid areas on the inline axis.
I also wanted some "margins" between the columns and rows, so I added a row-gap and a column-gap of 1rem with the shorthand.
As a result I added the following CSS to my div with the grid items inside it:
.card-section {
width: 100%;
display: grid;
justify-items: center;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
}
I know this is not exactly what OP wanted to achieve, but maybe it helps someone, who has the same problem as me and stumbles upon this question.
You can place invisible elements with the same class as the others (removed on example for exibition purposes) and height set to 0. With that, you will be able to justify the items to the very start of the grid.
Example
<div class="table-container">
<div class="table-content">
<p class="table-title">Table 1</p>
<p class="mesa-price">$ 20</p>
</div>
<!-- Make stuff justified start -->
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
<div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
</div>
Result
As #michael suggested, this is a limitation with current flexbox. But if you want to still use flex and justify-content: center;, then we can workaround this by adding a dummy li element and assign margin-left.
const handleResize = () => {
const item_box = document.getElementById('parentId')
const list_length = item_box.clientWidth
const product_card_length = 200 // length of your child element
const item_in_a_row = Math.round(list_length/product_card_length)
const to_be_added = item_in_a_row - parseInt(listObject.length % item_in_a_row) // listObject is the total number items
const left_to_set = (to_be_added - 1 ) * product_card_length // -1 : dummy item has width set, so exclude it when calculating the left margin
const dummy_product = document.querySelectorAll('.product-card.dummy')[0]
dummy_product.style.marginLeft = `${left_to_set}px`
}
handleResize() // Call it first time component mount
window.addEventListener("resize", handleResize);
Check this fiddle (resize and see ) or video for reference
One way to get the desired style with margins is to do the following:
#container {
display: flex;
justify-content: center;
}
#innercontainer {
display: flex;
flex: 0.9; -> add desired % of margin
justify-content: flex-start;
}
I ran into this problem while coding with React Native. There's an elegant solution that you can have using FlexBox. In my particular situation, I was trying to center three flex boxes (Flex: 2) inside another using alignItems. The solution I came up with was using two empty s, each with Flex: 1.
<View style={{alignItems: 'center', flexWrap: 'wrap', flexDirection: 'row'}}>
<View style={{flex: 1}} />
// Content here
<View style={{flex: 1}} />
</View>
Easy enough to convert to web / CSS.
The easiest way I've found to fix this is just simply add some place holders with visibility: hidden. That way it maintains the correct spacing as it wraps.
TL;DR
You can stuff some filler elements to the end of your container, and set visibility: hidden to make it invisible, and remember to set height: 0px to prevent the height be taken.
Demo
In the example below, you can click the button to watch the changes.
const container = document.getElementsByClassName('container')[0];
const item = document.createElement('div');
item.classList.add("item");
const filler = document.createElement('div')
filler.classList.add("filler");
item.appendChild(filler);
Array.from(Array(5).keys()).forEach(() => {
container.appendChild(item.cloneNode(true));
});
function onShowClick() {
const filler = document.getElementsByClassName('filler')
for (let i = 0; i < filler.length; i++) {
filler[i].style.border = "1px dashed #686868"
filler[i].style.visibility = "visible"
filler[i].style.height = "100px"
}
};
function onHideClick() {
const filler = document.getElementsByClassName('filler')
for (let i = 0; i < filler.length; i++) {
filler[i].style.border = "none"
filler[i].style.visibility = "hidden"
filler[i].style.height = "0px"
}
};
* {
box-sizing: border-box;
}
#root {
height: 400px;
width: 500px;
border: 1px solid #ff955a;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item {
padding: 5px;
}
.content-box {
height: 100px;
width: 100px;
padding: 10px;
background-color: lightblue;
border-radius: 10px;
}
.filler {
visibility: hidden;
height: 0px;
width: 100px;
}
<div id="root">
<button onclick="onShowClick()">Show</button>
<button onclick="onHideClick()">Hide</button>
<div class="container">
<div class="item">
<div class="content-box">1</div>
</div>
<div class="item">
<div class="content-box">2</div>
</div>
<div class="item">
<div class="content-box">3</div>
</div>
<div class="item">
<div class="content-box">4</div>
</div>
<div class="item">
<div class="content-box">5</div>
</div>
<div class="item">
<div class="content-box">6</div>
</div>
<div class="item">
<div class="content-box">7</div>
</div>
<div class="item">
<div class="content-box">8</div>
</div>
<div class="item">
<div class="content-box">9</div>
</div>
</div>
</div>

How to exclude a div (flex item) from flex calculations?

I have an html layout that I cannot modify since it's a 3d party library.
I have four divs and I want them to be inside a flexbox like that:
1------2------3
(where 2 is in the center and 4 is not visible at all).
I have created an example here.
It seems that it mostly work as I want to apart from the fact that the last div messes up the center position of the item number 2. Is there a way I can completely "exclude" it from the flex array by css even though it is in HTML?
.fc-toolbar {
background-color: #7CC2DD;
color: white;
height: 5rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.fc-left {
order: 0;
}
.fc-right {
order: 2;
}
.fc-center {
order: 1;
}
.fc-clear {
}
<div class="fc-toolbar">
<div class="fc-left">
<button type="button">
<
</button></div>
<div class="fc-right">
<button type="button">
>
</button>
</div>
<div class="fc-center">
<h2>May 2016</h2>
</div>
<div class="fc-clear"></div>
</div>
You can give the element position: absolute and it will be taken out of the flow, just as you would do with a non-flex element.
You can find the updated jsfiddle here.

Resources