In a recent project I had the need to render a two column layout where the articles filled up all available space and stacked left to right.
This is basically a masonry layout, but masonry has to fill top to bottom rather than left to right. I wanted it to fill left to right.
So, I implemented a solution with css floats and :nth-child(odd) and :nth-child(even) selectors, but this comes up against a problem when one of the columns has two short articles and the other side has a long one. The layout breaks! 🙁
I researched using css columns for the layout but this suffers from a few issues.
The only remaining option was to add a little bit of javascript to help out.
I found this great very simple jquery script but I didn’t want to use jquery, and it can be achieved very simply without it. So i rewrote it in vanilla javascript.
The main algorithm in ES2015 looks like this:
let leftColumnHeight = 0, rightColumnHeight = 0
let articles = document.querySelectorAll('.articles')
articles.forEach((el) => {
let outerHeight = el.outerHeight;
if (leftColumnHeight > rightColumnHeight) {
el.classList.add('right')
rightColumnHeight += outerHeight
} else {
leftColumnHeight += outerHeight
}
})
This runs through all the articles in your column and calculates the height of the article, if the right column total height is smaller then add a class that sets the article on the right.
The companion css to implement the layout is easy:
.article {
width: 50%;
float: left;
clear: left;
.article.right {
float: right;
clear: right;
}
Of course, this isn’t the whole story. If the screen is resized then column layout might no longer be correct, so I added a debounced listener on window.resize event. Also the el.outerHeight property doesn’t include margins, so we need to add an extra function for that.
You can find the full gist here or find it embedded below.
To use the class you can just instanciated it with the correct selector like this:
Hope it helps someone.