Flowing a list view into two columns

Columns. While it may seem like a good idea to a graphic designer, the idea of newspaper-style columns strikes fear in the hearts of themers everywhere. In particular, you may run into an instance where you need a list view to be A-L in the first column, then M-Z in the second column. I’ll walk you through a method for doing just that, using a little PHP. Note that this is for Drupal 7, but is easily adaptable for Drupal 6.

I will note first that there are a couple other quickie options that might work for your situation.

1. There are several javascript solutions available, which finds a midpoint in the text and wraps the chunks in divs. This works, but if a user has a plugin like NoScript enabled, this solution does not degrade gracefully.

2. There IS a CSS3 multicolumn solution, but it is largely unsupported. Currently it is only supported by Firefox 1.5+ and Safari 3.

So, eventually, there will be a simple CSS solution, but, for the time being, the cleanest option is server-side, using PHP.

In my example here, we’ll look at theming a list view to have multiple columns.

Say you have a view with a some items that we want to display in two columns. Views generates HTML that looks something like this:

<div class="view-columns">
  <div class="view-content">
    <div class="views-row views-row-1 views-row-odd views-row-first">...</div>
    <div class="views-row views-row-2 views-row-even">...</div>
    <div class="views-row views-row-3 views-row-odd">...</div>
    <div class="views-row views-row-4 views-row-even views-row-last">...</div>
  </div>
</div>

Here’s what that looks like just floating each row to the left. Looks like columns, but they are not in the order we want.

floated rows

Instead, we want something that looks like this:

newspaper style columns

To make this themable with CSS, we need the markup to look like this instead:

<div class="view-columns">
  <div id="leftcol" class="view-content">
    <div class="views-row views-row-1 views-row-odd views-row-first">...</div>
    <div class="views-row views-row-2 views-row-even">...</div>
  </div>
  <div id="rightcol" class="view-content">
    <div class="views-row views-row-3 views-row-odd">...</div>
    <div class="views-row views-row-4 views-row-even views-row-last">...</div>
  </div>
</div>

The solution to building this markup is with PHP in a views template. The next trick is to choose the right one! Editing your view, click on the Advanced tab, then on Theme: Information. We want to use the Display output template. So, for example, mine is called: views-view–columns–page.tpl.php.

First, we’ll make a preprocess function in our template.php file specific to that views template file that finds the number of view results and the halfway point.

This first function here is a helper function for Drupal 7 so you can add preprocess functions to specific views on the fly:

<?php
  function THEME_preprocess_views_view(&$vars) {
    if (isset($vars['view']->name)) {
      $function = 'THEME_preprocess_views_view__'.$vars['view']->name; 
      if (function_exists($function)) {
       $function($vars);
      }
    }
  }
?>

And here's the preprocess function:
<?php
  function THEME_preprocess_views_view__columns(&$variables) {
    $view = $variables['view'];
    // Create a variable that divides number of results in half and add one.
    $variables['half'] = ceil((count($view->result) / 2) + 1);
  }
?>

Once you have determined the $half variable, which is row number that starts the second column.

So let’s add the markup to the views tpl file.

Replace the lines:

<div class="view-content">
  <?php print $rows; ?>
</div>

with:

<?php
  // remove white space in html
  $rows = preg_replace('~>\s+<~', '><', $rows);
  // add the </div><div> in at the halfway point to separate the columns
  $search = '<div class="views-row views-row-' . $half;
  $replace = '</div><div id="rightcol" class="view-content"><div class="views-row views-row-' . $half;
  $rows = str_replace($search, $replace, $rows); 
?>
<div class="view-content" id="leftcol">
  <?php print $rows; ?>
</div>

This will wrap the first half of your results in a leftcol div and the second half in a rightcol div. From there, float your columns and add the necessary padding. Enjoy!