Home > database >  Create two column layouts to have dynamic row item height and filling some existing gap
Create two column layouts to have dynamic row item height and filling some existing gap

Time:11-15

I'm struggling a little bit to find a solution for a specific problem where I've been trying to solve by using HTML grid or flex-box. I want to build a two columns layout container where the row item height should be dynamic and if there's a gap, fill it with a new row item which is not part of the original list.

Let's suppose that I have a list of 6 items where I want to display in a container of two columns, like this:

.row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
}

.column {
  flex-basis: 50%;
}
<div >
  <div  style="background-color:#aaa;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
</div>

But in my problem I can have different image height, something like this:

.row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
}

.column {
  flex-basis: 50%;
}
<div >
  <div  style="background-color:#aaa;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="50">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="50">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
  <div  style="background-color:#bbb;">
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg" alt="Girl in a jacket" width="100" height="100">
  </div>
</div>

I would like that the row item to not use the whole row height, basically behaving as tiles when needed. The extra effort is to on the end of the column (neither left or right) to fill an extra gap with a new row item, which in my use case will be an advertisement tile.

This is one example where the tile height is dynamically set based on the image and the idea is to have an extra tile in case there's a gap.

enter image description here

Thanks in advance for any help!

CodePudding user response:

Is this the sort of thing you're after? If you know the image is 100px then you can use grid and get it to span 2 rows. This will remove available white space (mostly!)

.row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.25rem;
}

.column {
  background-color: #bbb;
  border: 1px solid black;
}

.color-a {
  background-color: #aaa;
}

.small>img {
  height: 50px;
}

.tall>img {
  height: 100px;
}

.tall {
  grid-row: span 2;
}
<div >
  <div >
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg">
  </div>
  <div >
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg">
  </div>
  <div >
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg">
  </div>
  <div >
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg">
  </div>
  <div >
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg">
  </div>
  <div >
    <img src="https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg">
  </div>
</div>

CodePudding user response:

I did something using js and the first dataset you provided which seems to achieve what you want :

// an array of HTMLElement which contains all the nodes you need to render
// can be anything, here images to match the dataset you provided
const nodes = [ 100, 50, 100, 50, 100, 100 ].map(height => {
    const img = document.createElement('img')

    img.src = 'https://revistacarro.com.br/wp-content/uploads/2021/03/aston-vantage-safety-car.jpg'
    img.width = 100
    img.height = height

    return img
})

// wait for the window to load
window.onload = () => {
    // get the container
    const container = document.getElementById('cols-container')

    // create the two cols (note that you can easily change this value if you want)
    const cols = (new Array(2)).fill(0).map(() => {
        const col = document.createElement('div')

        col.classList.add('column')

        return col
    })
    
    // add all the cols to the container
    // very important to do before the following loop, in order to access a relevant `getBoundingClientRect()`
    cols.forEach(col => container.appendChild(col))

    nodes.forEach(node => {
        // creates an array of tuples containing the col html element and its combined height and then grab the one with the littlest combined height
        const littlestCol = cols.map(e => [ e, Array.from(e.children).map(el => el.getBoundingClientRect().height).reduce((g, c) => g   c, 0) ]).reduce((g, c) => g[1] < c[1] ? g : c)[0]

        // add this node to the littles col
        littlestCol.appendChild(node)
    })

}
#cols-container {
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
}

.column {
    display: flex;
    flex-direction: column;
    gap: 2em;
}
<div id="cols-container"></div>

  • Related