I'm trying to balance the height between the columns in an image gallery I've made with the following code:
section { background: magenta; /* So you can see the area I don't want to appear */ } .gallery { width: 100%; line-height: 0; -webkit-column-count: 2; -webkit-column-gap: 0; -moz-column-count: 2; -moz-column-gap: 0; column-count: 2; column-gap: 0; }
<section class="gallery"> <div> <img src="http://lorempixel.com/800/1000/"> </div> <div> <img src="http://lorempixel.com/800/600/"> </div> <div> <img src="http://lorempixel.com/800/200/"> </div> <div> <img src="http://lorempixel.com/800/700/"> </div> <div> <img src="http://lorempixel.com/800/900/"> </div> <div> <img src="http://lorempixel.com/800/400/"> </div> <div> <img src="http://lorempixel.com/800/200/"> </div> <div> <img src="http://lorempixel.com/800/600/"> </div> <div> <img src="http://lorempixel.com/800/700/"> </div> <div> <img src="http://lorempixel.com/800/600/"> </div> <div> <img src="http://lorempixel.com/800/550/"> </div> <div> <img src="http://lorempixel.com/800/700/"> </div> <div> <img src="http://lorempixel.com/800/600/"> </div> <div> <img src="http://lorempixel.com/800/1000/"> </div> <div> <img src="http://lorempixel.com/800/700/"> </div> </section>
Here's a JSFiddle: https://jsfiddle.net/auan2xnj/1/
The images all have different heights but all have the same width. My column-fill
is set to balance
(as by default).
In the JSFiddle, it looks pretty good, but on the website I'm developing, some enormous gaps have appeared comparing the height of the columns. I'm guessing this is because of the orders the images have in the HTML, considering that's the exact same order they'll be put in the columns by the CSS. This is a screenshot from my project, using exactly the same code from the JSFiddle:
When I give the .gallery
element a hardcoded height
value, the columns always balance much better. This is a problem because in my website, images are added dynamically and I'm never going to know the exact height of all the galleries.
I would like to find a piece of code (whatever it is, I think I can implement some JS) that fixes this issue, either by reordering the images in the HTML so that the outcome is the best possible, or whatever way there is in order to set a height dynamically so that the problem is solved.
The column-span CSS property makes it possible for an element to span across all columns when its value is set to all .
Assign column-span to an element inside of the multi-column layout to make it a spanning element. The multi-column layout will resume with the next non-spanning element. The value of column-span can either be all or none . Set an element with column-span: all to make it span the columns.
EDIT: If you don't need to keep line-height: 0
you would simply use .gallery img {display:block}
and remove line-height, it's all you need. That would be the best. table-cell
and so on can have some side-effects. For example vertical-align: middle
leave a space under the images, and is just a hack.
https://jsfiddle.net/bruLwktv/
Challange accepted, here is the solution: ;)
The algorithm makes sure every images is loaded and then partition them into both coloumns in a way the have about the closest total height possible to create a minimal gap.
Using The greedy algorithm for the Partition problem to create Balanced partitions.
var gallery = document.getElementsByClassName("gallery")[0] var images = gallery.getElementsByTagName("img") var notLoaded = 0 window.onload = function() { for (var i = images.length; i--;) { if (images[i].width == 0) { // let the image tell us when its loaded notLoaded++ images[i].onload = function() { if (--notLoaded == 0) { allImgLoaded() } } } } // check if all images are already loaded if (notLoaded == 0) allImgLoaded() } function allImgLoaded() { // Partition images var imgs = partitionImages(images) // reorder DOM for (var i = images.length; i--;) { gallery.appendChild(imgs[i]) } } function partitionImages(images) { var groupA = [], totalA = 0 var groupB = [], totalB = 0 // new array width img and height var imgs = [] for (var i = images.length; i--;) { imgs.push([images[i], images[i].height]) } // sort asc imgs.sort(function(a, b) { return b[1] - a[1] }); // reverse loop for (var i = imgs.length; i--;) { if (totalA < totalB) { groupA.push(imgs[i][0]) totalA += imgs[i][1] } else { groupB.push(imgs[i][0]) totalB += imgs[i][1] } } return groupA.concat(groupB) }
section { background: magenta; /* So you can see the area I don't want to appear */ } .gallery { width: 100%; line-height: 0; -webkit-column-count: 2; -webkit-column-gap: 0; -moz-column-count: 2; -moz-column-gap: 0; column-count: 2; column-gap: 0; }
<section class="gallery"> <div><img src="http://lorempixel.com/800/1000/"></div> <div><img src="http://lorempixel.com/800/600/"></div> <div><img src="http://lorempixel.com/800/200/"></div> <div><img src="http://lorempixel.com/800/700/"></div> <div><img src="http://lorempixel.com/800/900/"></div> <div><img src="http://lorempixel.com/800/400/"></div> <div><img src="http://lorempixel.com/800/200/"></div> <div><img src="http://lorempixel.com/800/600/"></div> <div><img src="http://lorempixel.com/800/700/"></div> <div><img src="http://lorempixel.com/800/600/"></div> <div><img src="http://lorempixel.com/800/550/"></div> <div><img src="http://lorempixel.com/800/700/"></div> <div><img src="http://lorempixel.com/800/600/"></div> <div><img src="http://lorempixel.com/800/1000/"></div> <div><img src="http://lorempixel.com/800/700/"></div> </section>
Yeah, column implementation is bizarre right now.
If you get rid of "line-height: 0;" this problem becomes much less severe. I have no idea why. Except to say that the line-height thing is a hack to get rid of that omnipresent little space/margin at the bottom of images (remove the line-spacing rule in the jfiddle if you don't know what I'm talking about).
Supposedly that space/margin is there because HTML doesn't know an img element isn't text and so leaves room for the "tails" of characters like "y" and "g" which go below the line. This is a ridiculous bug which I think should've been fixed a decade ago. To me, this is up there with IE<9 levels of stupid implementation.
Anyway, rant over, a way to fix that space/margin without using the line-height hack:
img { vertical-align: middle; }
Also, it's not an issue if you're positive all your images will be the same width, but in the fiddle the gap problem is made worse by images overlapping the widths of the columns. You should set
img { width: 100% // and, for insurance, I also add: height: auto; }
to make sure you're fitting the images into the columns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With