0

I have a list of articles which have to be presented on two columns.

Everything is ok except that the height of articles could be very different and the result is that i can have a column a lot more longer that the other one.

It’s not a big issue but the result is not clean. You can see the picture attached to see.

It’s because, before i make a loop, i count all the articles and divide the result by 2 :

This is the code (a little bit more complicated in my template but it’s just html stuff)

// make a query to get the number of articles
$count_articles = 20;
$i = 0;

if ($i == 0) {
    echo '<div class="column_1">';
}

if ($i <= 10) {
    echo '<div style="float:left;width:350px;">';
    echo $articles
    echo '</div><div style="clear:both;"></div>';
    $i++;
}

if ($i == 11) {
    //close column_1, open column_2
    echo '</div><div class="column_2">';
}

if ($i >= 11 && $i <= 20) {
    echo '<div style="float:right;width:358px">';
    echo $articles
    echo '</div>';

    if ($i ==20) {
        //close column_2
        echo '</div>';
        }
    $i++;
}

Do you know a way to get the height of articles before i make the loop or an other way to do that (with javascript maybe) ?

enter image description here

Sébastien Gicquel
  • 4,227
  • 7
  • 54
  • 84

4 Answers4

3

Two approaches: server-side or client-side. (Also the float: left solution, but that's unpredictable and hard to control, or the new CSS3 column layout maybe).

Both of them employ the same algorithm of dividing articles into two (approximately) equal height columns:

let totalHeight, leftColumnHeight = 0
let leftColumn, rightColumn = []

foreach article do
    totalHeight += getHeightOf(article)
end

foreach article do
    if leftColumnHeight < totalHeight / 2 then
        push article to leftColumn
        leftColumnHeight += getHeightOf(article)
    else
        push article to rightColumn  
end

The difference between the two approaches is the implementation of getHeightOf.

Client-side, using jQuery, you can use $('#articleId').outerHeight(). This has the advantage of being precise, but mandates that all the articles be rendered beforehand (outerHeight won't work for hidden elements or elements not attached to the DOM), thus leading to displaying a wrong layout to the user before the algorithm kicks in (some time after $(document).ready). You can alleviate this by rendering the articles off-screen (position: absolute; left: -9999px;) but then users could be staring at an empty list of articles until the JavaScript code kicks in.

Server-side you can approximate the height of the articles. You don't really need pixel heights, just the total number of lines of text, depending on maximum line width. This is difficult if you also have inline images and could potentially result in different values than what's actually displayed in browsers. But if you manage to come up with a decent enough algorithm you don't have to worry about sudden layout changes visible to the end-user or delayed rendering.

So, as a conclusion, there's no perfect solution as the problem has a lot of variables, most of them related to the actual content of the articles. The JavaScript one is the most popular but in my opinion has some pretty hard-hitting trade-offs that end users won't appreciate. The server-side one is tricky to implement and somewhat breaks MVC conventions if not done right (logic in views, even if it's layout logic).

Community
  • 1
  • 1
Sergiu Paraschiv
  • 9,929
  • 5
  • 36
  • 47
2

So I just put something together quickly to do this with jQuery. Probably needs a bit of work to fit your situation, but the basic idea seems to work well.

So basically I have a <div> with the articles in. Then also a <div> for each column. Like this:

<div id="col1"></div>
<div id="col2"></div>
<div id="temp">
    <!--Articles Here-->
</div>

I then use some jQuery to loop through each article, check the height of each column and append() the article to the shortest column.

$( document ).ready(function() {

    $( ".article" ).each(function( index ) {
        var col1Height= $("#col1").height();
        var col2Height= $("#col2").height();

        if(col1Height < col2Height){
            $("#col1").append($(this));
        }else{
            $("#col2").append($(this));
        }
    });

});

This should make the columns fairly even in height, without too much effort. I'm sure there's other ways which will make the columns even closer in height, but as I say, I think this is a nice simple solution.

The jsFiddle is here: http://jsfiddle.net/4kpLr/1/

AndrewPolland
  • 3,041
  • 4
  • 28
  • 39
  • Accepted answer : really nice and simple solution. One thing that is more difficult to solve. Originally, the articles are ordered alphabetically (with php) and now the order is wrong. I guess it is really hard to combine alphabetical order and and an order based on the height of each column. – Sébastien Gicquel Jul 07 '14 at 13:15
  • 1
    @SébastienGicquel Glad you like it. :) I think, as long as you list the elements in alphabetical order to begin with, that this will give you closest you're going to get. As it will order those higher in the list first, so they still get priority. – AndrewPolland Jul 07 '14 at 13:24
  • Maybe he would like the "first" articles to be on top, not on first column. This would be harder to achieve. – Sergiu Paraschiv Jul 07 '14 at 13:29
  • @SergiuParaschiv Sorry, probably didn't explain it well. But that's essentially what he'll get with this. It will be alphabetically ordered going downwards on the 2 columns combined, but there won't be perfect lines of separation going down. – AndrewPolland Jul 07 '14 at 13:35
  • Cool, sorry, you are right, that's exactly what your version does. – Sergiu Paraschiv Jul 07 '14 at 13:36
  • @SergiuParaschiv Originally, the alphabetical order was made inside each column, a, b, c, d in column 1, and e, f, g, h in colum_2. and i see it is much harder to achieve. – Sébastien Gicquel Jul 07 '14 at 14:05
0

You can work with jquery for example

$('.classofblock').each(function(index, elem) {
    var heightOfElem = $(elem).height();
    // you can even console this height
    console.log(heightOfElem);
    // or do something else things
    // if you want set height, so do next
    $(elem).css('height', 1000'); // it's automatically in px
});

But you should wrapped this code in

$(document).ready(function() {
    // here is your code...
});
Aziz
  • 161
  • 1
  • 2
  • 11
0

For 2 columns with equilibrated height add below jquery code

$(document).ready( function(){ var oneHeight = $(".column_1").height(); var twoHeight = $(".column_2").height(); if (oneHeight > twoHeight){ $(".column_2").height(oneHeight)} else{ $(".column_1").height(twoHeight)}; })

codeee
  • 397
  • 3
  • 7