RegularEverydayNormalGuy RegularEverydayNormalGuy - 3 years ago 205
CSS Question

Flex-box items don't re-render correctly on chrome

I came across different browser behaviors when programmatically changing dimensions or the position property of flex-box items. The layout doesn't return to its original state on Chrome.

In this pen, when the user clicks an image, I'm changing the item position to absolute and modifying its width and height (actually it could be anything that affects the layout, e.g. padding). When the user clicks again, I rollback the changes, so I expect the layout to reset accordingly.

A js workaround is to force the layout to re-render by quickly changing the items padding by 1px back and forth, but I wonder:

Why does this happen and is there a css-only workaround?

Chrome



When the user clicks again and the flex item style is reset, flex-basis is not taken into account anymore and the layout is broken.
Chrome

Firefox



When the user clicks again and the flex item style is reset, all items are properly rendered.
Firefox



new Vue({
el: '#app',

data() {
return {
activeImageIndex: null,
images: [{
url: 'https://unsplash.it/600',
style: {
flexBasis: '50%'
}
},
{
url: 'https://unsplash.it/600',
style: {
flexBasis: '50%'
}
},
{
url: 'https://unsplash.it/600',
style: {
flexBasis: '30%'
}
},
{
url: 'https://unsplash.it/600',
style: {
flexBasis: '30%'
}
},
{
url: 'https://unsplash.it/600',
style: {
flexBasis: '30%'
}
},
]
}
},


methods: {
onClick(index) {
this.activeImageIndex = this.activeImageIndex === index ? null : index
}
},
})

#app {
width: 800px;
}

.ig-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
margin: 0 -5px;
position: relative;
height: 500px;
}

.ig-item {
flex: 1 1;
padding: 10px;
box-sizing: border-box;
}

.ig-item:hover>div {
border-color: green;
}

.ig-item.active {
position: absolute;
z-index: 3;
width: 100%;
height: 100%;
}

.ig-item.active img:hover {
cursor: zoom-out;
}

.ig-item>div {
height: 100%;
width: 100%;
background-color: blue;
position: relative;
border: 5px solid gainsboro;
overflow: hidden;
}

.ig-item .ig-overlay {
position: absolute;
height: 100%;
width: 100%;
z-index: 3;
background-color: rgba(0, 0, 0, 0.4);
color: #fff;
font-size: 3rem;
display: flex;
align-items: center;
text-align: center;
}

.ig-item .ig-overlay span {
width: 100%;
}

.ig-item img {
height: 100%;
width: 100%;
object-fit: cover;
position: absolute;
z-index: 2;
transition: transform .3s;
}

.ig-item img:hover {
cursor: zoom-in;
}

.ig-item .loading-overlay.ig-loading {
position: absolute;
z-index: 1;
}

.ig-item .loading-overlay.ig-loading .loading-icon:after {
top: -2.5em;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>

<div id="app">
<div class="ig-container">
<div class="ig-item" :class="{active: activeImageIndex === index}" :style="image.style" v-for="(image, index) in images">
<div>
<img @click="onClick(index)" title="" :src="image.url">
</div>
</div>
</div>
</div>




Answer Source

The issue looks unusual but the fix is rather simple. Add min-height: 0 to .ig-item.

Possibly the issue seems to be how browsers manifest the fact that the min-height property for flex items is auto by default.

See demo below:

new Vue({
  el: '#app',

  data() {
    return {
      activeImageIndex: null,
      images: [{
          url: 'https://unsplash.it/600',
          style: {
            flexBasis: '50%'
          }
        },
        {
          url: 'https://unsplash.it/600',
          style: {
            flexBasis: '50%'
          }
        },
        {
          url: 'https://unsplash.it/600',
          style: {
            flexBasis: '30%'
          }
        },
        {
          url: 'https://unsplash.it/600',
          style: {
            flexBasis: '30%'
          }
        },
        {
          url: 'https://unsplash.it/600',
          style: {
            flexBasis: '30%'
          }
        },
      ]
    }
  },


  methods: {
    onClick(index) {
      this.activeImageIndex = this.activeImageIndex === index ? null : index
    }
  },
})
#app {
  width: 800px;
}

.ig-container {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  margin: 0 -5px;
  position: relative;
  height: 500px;
}

.ig-item {
  flex: 1 1;
  padding: 10px;
  box-sizing: border-box;
  min-height: 0; /* ADDED */
}

.ig-item:hover>div {
  border-color: green;
}

.ig-item.active {
  position: absolute;
  z-index: 3;
  width: 100%;
  height: 100%;
}

.ig-item.active img:hover {
  cursor: zoom-out;
}

.ig-item>div {
  height: 100%;
  width: 100%;
  background-color: blue;
  position: relative;
  border: 5px solid gainsboro;
  overflow: hidden;
}

.ig-item .ig-overlay {
  position: absolute;
  height: 100%;
  width: 100%;
  z-index: 3;
  background-color: rgba(0, 0, 0, 0.4);
  color: #fff;
  font-size: 3rem;
  display: flex;
  align-items: center;
  text-align: center;
}

.ig-item .ig-overlay span {
  width: 100%;
}

.ig-item img {
  height: 100%;
  width: 100%;
  object-fit: cover;
  position: absolute;
  z-index: 2;
  transition: transform .3s;
}

.ig-item img:hover {
  cursor: zoom-in;
}

.ig-item .loading-overlay.ig-loading {
  position: absolute;
  z-index: 1;
}

.ig-item .loading-overlay.ig-loading .loading-icon:after {
  top: -2.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>

<div id="app">
  <div class="ig-container">
    <div class="ig-item" :class="{active: activeImageIndex === index}" :style="image.style" v-for="(image, index) in images">
      <div>
        <img @click="onClick(index)" title="" :src="image.url">
      </div>
    </div>
  </div>

</div>

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download