Remove a pseudo-element when there is only one child element

我怕爱的太早我们不能终老 提交于 2019-12-01 23:29:26

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>

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

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

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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!