I'm trying to separate children of a container div into left/right columns, with no vertical whitespace below or above the children items.
I'll explain the setup and provide a picture of the desired result, and then I’ll explain every solution I’ve tried and why each solution doesn’t seem to work.
The Setup
I have a container with children <div>
's that have a class of either “left” or “right.” Here's the code (I am bound to this HTML structure):
<div >
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >R-Pellentesque nec tellus at tellus</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >L-Pellentesque nec tellus at tellus scelerisque rutrum ut quis nibh. Aliquam nisi nisl, finibus eu condimentum ac, pretium quis augue.</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
</div>
I need the children to display in two columns. If the child has class .left
it goes in the left column. If it has .right
it goes in the right column.
I need the children to fill up their respective column starting from the top, with no white-space above or below each child.
Each child's height is determined by its text contents, so I cannot set a fixed height for each child.
Here is the desired result (styling added for emphasis): Desired Outcome Visual
And a reminder, I'm bound by (1) the previous HTML structure and (2) height of child <div>
cannot be fixed.
Here's what I've tried, along with why it didn't work:
Option 1: CSS Float
I set each child’s width
to 50% and display
to inline-block
, and apply a float:left
to the .left
children and float: right
to the .right
children.
This almost works, except if the first two children have a .right
class. You can see the second .right
child floats to take up the remaining 50% left of the first .right
child.
.container > div {color: white; border-bottom: 3px solid white;}
.container {
overflow: auto;
}
.col {
width: 50%;
}
.right {
background: #999;
float: right;
}
.left {
background: #000;
float: left;
}
<div >
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >R-Pellentesque nec tellus at tellus</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >L-Pellentesque nec tellus at tellus scelerisque rutrum ut quis nibh. Aliquam nisi nisl, finibus eu condimentum ac, pretium quis augue.</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
</div>
Option 2: CSS-Grid
I apply some CSS grid
options. After much research and trial and error, the closest I could come up with is this:
.container {
display: grid;
grid-template-columns: auto auto;
grid-auto-flow: column;
}
.right {
background: #999;
grid-column-start: 2;
}
.left {
background: #000;
grid-column-start: 1;
}
.container > div {color: white; border-bottom: 3px solid white;}
<div >
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >R-Pellentesque nec tellus at tellus</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >L-Pellentesque nec tellus at tellus scelerisque rutrum ut quis nibh. Aliquam nisi nisl, finibus eu condimentum ac, pretium quis augue.</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
</div>
Again, this comes very close and even solves the problem pointed out in option 1. BUT you’ll see when a .left
child has a larger height than a .right
child, it causes the .right
child to add white-space to fill the row.
Option 3: Desandro Masonry
I tried the Desandro Masonry option, but the children are output in their static order, with no respect to left/right. And I’ve not found a clear option in the library that allows to tap into a class and assign it into a column based on .right
or .left
class.
var msnry = new Masonry( '.container', {
itemSelector: '.col',
});
.container {
display: grid;
}
.col {
width: 50%;
}
.container > div {color: white; border-bottom: 3px solid white;}
.right {
background: #999;
}
.left {
background: #000;
}
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
<div >
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >R-Pellentesque nec tellus at tellus</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >L-Pellentesque nec tellus at tellus scelerisque rutrum ut quis nibh. Aliquam nisi nisl, finibus eu condimentum ac, pretium quis augue.</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
</div>
Option 4: Flexbox
I tried a number of Flexbox
combinations, and I couldn’t get any to work. My research suggests FlexBox
wouldn’t do what I needed anyway. But maybe I’m missing something?
Option 5: CSS Column-Count property
Just for kicks, I messed around with the CSS Column-Count
property, but there’s no way to assign children to a left/right column.
===
At this point I’m stuck. I’ve tried so many variations and read so many stacks today that my brain is pooped.
I would be very grateful for any ideas that will achieve the desired outcome. It's probably that I'm just missing something in one of the solutions I've already tried.
Thanks for your time!
CodePudding user response:
If you are allowed to use a little JS (and I assume you are since one of your trials included a jquery library) you can calculate the vertical position of each element, position each element absolutely, and at the end set the height of container if its relative positioning is important.
let leftH = 0;
let rightH = 0;
const els = document.querySelectorAll('.container > *');
els.forEach(el => {
if (el.getAttribute('class').includes('left')) {
el.style.top = leftH 'px';
leftH = el.offsetHeight;
} else {
el.style.top = rightH 'px';
rightH = el.offsetHeight;
}
});
document.querySelector('.container').style.height = ((leftH > rightH) ? leftH : rightH) 'px';
.container {
width: 100vw;
position relative;
}
.container>* {
width: 50%;
border: 1px solid;
position: absolute;
}
.right {
margin-left: 50%;
}
<div >
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >R-Pellentesque nec tellus at tellus</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
<div >R-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum consequat odio eget felis mollis placerat. Donec consequat tincidunt nisl sit amet iaculis. In sit amet nisl purus.</div>
<div >L-Pellentesque nec tellus at tellus scelerisque rutrum ut quis nibh. Aliquam nisi nisl, finibus eu condimentum ac, pretium quis augue.</div>
<div >L-Nulla viverra lorem risus, nec consectetur urna pretium sed. Vestibulum bibendum, tortor vel viverra consequat, urna purus pulvinar odio, sed rutrum justo risus in justo.</div>
<div >R-Vivamus in lacus sed dolor ullamcorper blandit non fermentum tortor.</div>
</div>