问题
I use an invisible pseudo-element (::after
) that occupies the last slot in the container. But if there is only one element, I'd like to position it in the center.
So in order to do it I need to "remove" the pseudo-element in that case.
How do I do it if it's possible?
.main {
background: #999;
margin: 0 auto;
width: 500px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.box {
background: #7ab9d7;
color: #555;
height: 30px;
width: 30%;
margin-bottom: 30px;
text-align: center;
font-size: 30px;
padding-top: 120px;
}
.main::after {
height: 0;
width: 30%;
content: "";
}
<div class="main">
<div class="box">1</div>
</div>
P.S. Example was taken from here but decreased number of children to 1.
P.S. P.S. If many divs - looks like this
If one div - looks like this
回答1:
How to not display ::after content if one child in parent?
There is no CSS way (today, but with a parent selector it might some day) to remove the pseudo based on the amount of flex items, even if it is a child and act as a flex item, it still can't be explicit targeted with anything else than through its parent.
A simple workaround would be to use a left margin combined with transform
when there is only 1 element, here using the only-child
selector.
One could use position: relative; left: 50%
as well, though margin-left
is one line less
This will work with any number of elements, regardless their size.
Stack snippet
.main {
background: #999;
margin: 0 auto;
width: 500px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.box {
background: #7ab9d7;
color: #555;
height: 30px;
width: 30%;
margin-bottom: 30px;
text-align: center;
font-size: 30px;
padding-top: 120px;
}
.main::after {
height: 0;
width: 30%;
content: "";
}
.main div:only-child { /* added rule */
margin-left: 50%;
transform: translateX(-50%);
}
<div class="main">
<div class="box">1</div>
</div>
<br>
<div class="main">
<div class="box">1</div>
<div class="box">1</div>
</div>
<br>
<div class="main">
<div class="box">1</div>
<div class="box">1</div>
<div class="box">1</div>
<div class="box">1</div>
</div>
<br>
Updated based on a comment, where, if there is 2 items, they as well should be centered.
To be able to accomplish that with the existing markup/CSS, we also need to make use of the ::before
pseudo.
With a couple of clever CSS selectors, we can count if there is 1 or 2 elements.
This work like that, when 1 or 2 items, auto margin is used, and with the order
property the items are positioned after the ::after
, which now is 100% wide and will push the items to a new row, and there they will not be affected by either pseudo.
For 3 items or more, they will positioned before both pseudo's, where the ::before
now will act as the ::after
did in the initial solution, and left align items on the last row.
Fiddle demo
Stack snippet
.main {
background: #999;
margin: 0 auto;
width: 500px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.box {
background: #7ab9d7;
color: #555;
height: 30px;
width: 30%;
margin-bottom: 30px;
text-align: center;
font-size: 30px;
padding-top: 120px;
}
.main::before {
height: 0;
width: 30%;
content: "";
order: 1;
}
.main::after {
height: 0;
width: 100%;
content: "";
order: 2;
}
/* new rules */
.main div:only-child, /* if 1 only */
.main div:first-child:nth-last-child(2) + div { /* if 2, the 2nd*/
order: 3;
margin-left: auto;
margin-right: auto;
}
.main div:first-child:nth-last-child(2) { /* if 2, the 1st */
order: 3;
margin-left: auto;
}
<div class="main">
<div class="box">1</div>
</div>
<br>
<div class="main">
<div class="box">1</div>
<div class="box">2</div>
</div>
<br>
<div class="main">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
</div>
<br>
<div class="main">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
</div>
回答2:
Try this. Updated the fiddle. Removed the pseudo element that you used as a placeholder. Below is the updated CSS.
.main {
background: #999;
margin: 0 auto;
width: 500px;
display: flex;
flex-wrap: wrap;
}
.box {
background: #7ab9d7;
color: #555;
height: 30px;
width: 30%;
margin-bottom: 30px;
text-align: center;
font-size: 30px;
padding-top: 120px;
}
.box:not(:nth-child(3n)) {
margin-right: 5%;
}
.box:only-child {
margin-right: auto;
margin-left: auto;
}
With multiple .boxes
With single .box
回答3:
There is no simple solution to this problem using flexbox, because flexbox isn't designed for this sort of layout.
Notice that you need a hack – the pseudo-element – to achieve the main layout.
So it's not surprising that you need an even more complex hack – see the other answers – to build a variation of that layout.
Flexbox is designed for flexibility through the distribution of free space. As soon as you start to confine this flexibility (e.g., forcing a flex item to stay in a column by bringing in a fake item to hold a position), flexbox starts to break.
This concept is further explained here: Targeting flex items on the last row
This is a problem / limitation that the people at the W3C are aware of, which is one reason they rolled out CSS Grid Layout, which solves this problem cleanly and easily:
multiple items
.main {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 150px 150px;
grid-row-gap: 30px;
grid-column-gap: 3%;
width: 500px;
background: #999;
margin: 0 auto;
}
.box {
display: flex;
align-items: flex-end;
justify-content: center;
text-align: center;
background: #7ab9d7;
color: #555;
font-size: 30px;
}
.box:only-child {
grid-column: 2 / 3;
}
<div class="main">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
</div>
single item
.main {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 150px 150px;
grid-row-gap: 30px;
grid-column-gap: 3%;
width: 500px;
background: #999;
margin: 0 auto;
}
.box {
display: flex;
align-items: flex-end;
justify-content: center;
text-align: center;
background: #7ab9d7;
color: #555;
font-size: 30px;
}
.box:only-child {
grid-column: 2 / 3;
}
<div class="main">
<div class="box">1</div>
</div>
jsFiddle demo
回答4:
To achieve this first you need to remove the ::after
, then you will need to justify-content: center;
by default and target for every last box to left center using margin: 0 auto 30px 0;
However the last box can also be the first box, to overwrite this use:
.box:first-child {
margin: 0 auto 30px auto !important;
}
To get the extra padding within each box you will need to add extra div
and add the blue background separately.
.main {
background: #999;
margin: 0 auto;
width: 500px;
display: flex;
flex-wrap: wrap;
/*justify-content: space-between;*/
justify-content: center;
align-items: center;
}
.box {
background: #7ab9d7;
color: #555;
height: 30px;
/* width: 30%; */
margin-bottom: 30px;
text-align: center;
font-size: 30px;
padding-top: 120px;
/* flex: auto; */
/* flex: 1; */
flex-basis: 33.33%;
}
.box:last-child{
margin: 0 auto 30px 0;
}
.box:first-child {
margin: 0 auto 30px auto !important;
}
/*
.main::after {
height: 0;
width: 30%;
content: "";
}
*/
<div class="main">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
</div>
来源:https://stackoverflow.com/questions/46629565/remove-a-pseudo-element-when-there-is-only-one-child-element