Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Balancing height in column-count CSS columns that contain only images

Tags:

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).

The problem:

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:

enter image description here

Behaviour:

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.

Request:

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.

like image 446
fnune Avatar asked Jan 09 '16 17:01

fnune


People also ask

How we can span one or more pieces of content across multiple columns?

The column-span CSS property makes it possible for an element to span across all columns when its value is set to all .

How do I span all columns in CSS?

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.


2 Answers

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>
like image 192
CoderPi Avatar answered Sep 17 '22 13:09

CoderPi


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.

like image 27
Lee Saxon Avatar answered Sep 18 '22 13:09

Lee Saxon