ChatGPT解决这个技术问题 Extra ChatGPT

Flexbox: 4 items per row

I'm using a flex box to display 8 items that will dynamically resize with my page. How do I force it to split the items into two rows? (4 per row)?

Here is a relevant snip:

(Or if you prefer jsfiddle - http://jsfiddle.net/vivmaha/oq6prk1p/2/)

.parent-wrapper { height: 100%; width: 100%; border: 1px solid black; } .parent { display: flex; font-size: 0; flex-wrap: wrap; margin: -10px 0 0 -10px; } .child { display: inline-block; background: blue; margin: 10px 0 0 10px; flex-grow: 1; height: 100px; }

[Update] This question was based on a poor unresponsive design. If you find yourself using one of the answers below, be careful. In a good design, you'd have more items on wider screens, and fewer on smaller screens. Forcing 4 items for all screen sizes will only look appealing on a narrow range of screen widths.

M
Michael Benjamin

You've got flex-wrap: wrap on the container. That's good, because it overrides the default value, which is nowrap (source). This is the reason items don't wrap to form a grid in some cases.

In this case, the main problem is flex-grow: 1 on the flex items.

The flex-grow property doesn't actually size flex items. Its task is to distribute free space in the container (source). So no matter how small the screen size, each item will receive a proportional part of the free space on the line.

More specifically, there are eight flex items in your container. With flex-grow: 1, each one receives 1/8 of the free space on the line. Since there's no content in your items, they can shrink to zero width and will never wrap.

The solution is to define a width on the items. Try this:

.parent { display: flex; flex-wrap: wrap; } .child { flex: 1 0 21%; /* explanation below */ margin: 5px; height: 100px; background-color: blue; }

With flex-grow: 1 defined in the flex shorthand, there's no need for flex-basis to be 25%, which would actually result in three items per row due to the margins.

Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to enforce a wrap. In this case, with flex-basis: 21%, there's plenty of space for the margins, but never enough space for a fifth item.


I like this because it worked for me, but “plenty of space” makes me feel a little uneasy. Is there some tiny window or situation where this “estimation” might fail me?
“if you put in very large margins, the layout may break” That’s an example of a scenario I’d like to avoid. I don’t care about practicality; entertain my adoration for perfection. Is there any way to be more exact?
You can also use flex: 1 0 calc(25% - 10px);
Great answer! A small improvement I use is margin: 2%; instead of a fixed pixel number in order to prevent the boxes from jumping to a new line on small devices/resolutions. the general formula is (100 - (4 * box-width-percent)) / 8% for different box widths
In case anyone forgets, the flex property here is shorthand for flex-grow: 1; flex-shrink: 0; flex-basis: 21%;
I
Iharob Al Asimi

Add a width to the .child elements. I personally would use percentages on the margin-left if you want to have it always 4 per row.

DEMO

.child {
    display: inline-block;
    background: blue;
    margin: 10px 0 0 2%;
    flex-grow: 1;
    height: 100px;
    width: calc(100% * (1/4) - 10px - 1px);
}

Good idea. I used this idea with minor edits. jsfiddle.net/vivmaha/oq6prk1p/6
Ah. This actually doesn't solve my problem. See here for an example: jsfiddle.net/vivmaha/oq6prk1p/7. I do not have the liberty of using percentages for my margin. They must be a fixed width. This means that the magic 23% that we chose won't work since it doesn't take the fixed margin into account.
Yes, calc solves it! 'calc(100% * (1/4) - 10px - 1px)'. See jsfiddle.net/vivmaha/oq6prk1p/9
Then what's the use of flex in this, still?
What about responsive design? We don't want to put a fixed width on child
C
Capy

Here is another apporach.

You can accomplish it in this way too:

.parent{
  display: flex;
  flex-wrap: wrap;
}

.child{
  width: 25%;
  box-sizing: border-box;
}

Sample: https://codepen.io/capynet/pen/WOPBBm

And a more complete sample: https://codepen.io/capynet/pen/JyYaba


What about if I have less then 4 child? I dislike that blank empty space, the child are not responsive...
The other answer by Muddasir Abbas uses only flex settings, so I think it's a cleaner solution.
s
shanomurphy

I would do it like this using negative margins and calc for the gutters:

.parent {
  display: flex;
  flex-wrap: wrap;
  margin-top: -10px;
  margin-left: -10px;
}

.child {
  width: calc(25% - 10px);
  margin-left: 10px;
  margin-top: 10px;
}

Demo: https://jsfiddle.net/9j2rvom4/

Alternative CSS Grid Method:

.parent {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 10px;
}

Demo: https://jsfiddle.net/jc2utfs3/


this one not only works but keeps the div centered too. Better for my situations than the flexbox ones
I believe that css grid is supported well enough now to safely use it
t
timthedev07

For more detail you can follow this Link

.parent{ display: flex; flex-wrap: wrap; } .parent .child{ flex: 1 1 25%; /*Start Run Code Snippet output CSS*/ padding: 5px; box-sizing: border-box; text-align: center; border: 1px solid #000; /*End Run Code Snippet output CSS*/ }

1
2
3
4
5
6
7
8


Thanks, to easily control the amount of columns you can flex: 1 1 calc(100% / 6)
J
Joseph Cho

I believe this example is more barebones and easier to understand then @dowomenfart.

.child {
    display: inline-block;
    margin: 0 1em;
    flex-grow: 1;
    width: calc(25% - 2em);
}

This accomplishes the same width calculations while cutting straight to the meat. The math is way easier and em is the new standard due to its scalability and mobile-friendliness.


em is not really the new standard. It can be a pain in the ass, and designers don't design with EM's in mind.
M
Mateus Manosso Barszcz

.parent-wrapper { height: 100%; width: 100%; border: 1px solid black; } .parent { display: flex; font-size: 0; flex-wrap: wrap; margin-right: -10px; margin-bottom: -10px; } .child { background: blue; height: 100px; flex-grow: 1; flex-shrink: 0; flex-basis: calc(25% - 10px); } .child:nth-child(even) { margin: 0 10px 10px 10px; background-color: lime; } .child:nth-child(odd) { background-color: orange; } Document

;)


If you can please add some context about why this answers the OP question rather than just posting code.
if you add justify-content: space-evenly; to the prent, then they align nicely and you don't have to do the calc on the child. Thanks for the example, though!
z
zurfyx

Flex wrap + negative margin

Why flex vs. display: inline-block?

Flex gives more flexibility with elements sizing

Built-in white spacing collapsing (see 3 inline-block divs with exactly 33% width not fitting in parent)

Why negative margin?

Either you use SCSS or CSS-in-JS for the edge cases (i.e. first element in column), or you set a default margin and get rid of the outer margin later.

Implementation

https://codepen.io/zurfyx/pen/BaBWpja

<div class="outerContainer">
    <div class="container">
        <div class="elementContainer">
            <div class="element">
            </div>
        </div>
        ...
    </div>
</div>
:root {
  --columns: 2;
  --betweenColumns: 20px; /* This value is doubled when no margin collapsing */
}

.outerContainer {
    overflow: hidden; /* Hide the negative margin */
}

.container {
    background-color: grey;
    display: flex;
    flex-wrap: wrap;
    margin: calc(-1 * var(--betweenColumns));
}

.elementContainer {
    display: flex; /* To prevent margin collapsing */
    width: calc(1/var(--columns) * 100% - 2 * var(--betweenColumns));
    margin: var(--betweenColumns);
}

.element {
    display: flex;
    border: 1px solid red;
    background-color: yellow;
    width: 100%;
    height: 42px;
}

S
Samet Akpınar

you can try this

.parent-wrapper { height:100%; width:100%; border: 1px solid black; } .parent { display: grid; font-size: 0; grid-template-columns: 25% 25% 25% 25%; } .child { background:blue; flex-grow: 1; height:100px; margin: 10px; margin-bottom: 0; } .child:last-child { margin-bottom: 10px; }

https://jsfiddle.net/samet19/gdntwLhb/


The questioner ask for flex but you answered grid.
J
Jef

Here's another way without using calc().

// 4 PER ROW
// 100 divided by 4 is 25. Let's use 21% for width, and the remainder 4% for left & right margins...
.child {
  margin: 0 2% 0 2%;
  width: 21%;
}

// 3 PER ROW
// 100 divided by 3 is 33.3333... Let's use 30% for width, and remaining 3.3333% for sides (hint: 3.3333 / 2 = 1.66666)
.child {
  margin: 0 1.66666% 0 1.66666%;
  width: 30%;
}

// and so on!

That's all there is to it. You can get fancy with the dimensions to get a more aesthetic sizes but this is the idea.


I haven't tested this, but the CSS syntax is wrong (CSS values are not quoted, and declarations should end in a semi-colon) so it definitely won't work in its current form.