This snippet allows you to format your checkboxes/radio buttons into multiple columns.

Add the following to hook_theme():

function mymodule_theme() {
  return array(
...
     # New!
     'multicolumn_options' => array(
       'arguments' => array('element' => NULL),
     ),
...
  );
}

And the following somewhere in your .module file:

/**
 * Output a list of options in the number of columns specified by the element's
 * #columns value.
 */
function theme_multicolumn_options($element) {
  // Initialize variables.
  $output = '';
  $total_columns = $element['#columns'];
  $total_options = count($element['#options']);
  $options_per_column = ceil($total_options / $total_columns);
  $keys = array_keys($element['#options']);
  $type = $element[$keys[0]]['#type'];

  // Start wrapper div.
  $output .= '<div class="multicolumn-options-wrapper">';
  $current_column = 1;
  $current_option = 0;

  while ($current_column <= $total_columns) {
    // Start column div.
    $output .=  '<div class="multicolumn-options-column" style="width: ' . 100 / $total_columns . '%; float: left">';

    // Keep looping through until the maximum options per column are reached,
    // or you run out of options.
    while ($current_option < $options_per_column * $current_column &&
           $current_option < $total_options) {

      // Output as either check or radio button depending on the element type.
      $output .= theme($type, $element[$keys[$current_option]]);
      $current_option++;
    }

    // End column div.
    $output .= '</div>';
    $current_column++;
  }

  // End wrapper div.
  $output .= '</div>';
  $output .= '<div class="clear-block"></div>';

  return $output;
}

Now, on any form checkboxes or radios element where you want to format the options into multiple columns, add a #theme and #columns property.

Change:

$element['example'] = array(
  '#type' => 'checkboxes',
  '#title' => t('Example checkboxes'),
  '#options' => $options,
);

To:

$element['example'] = array(
  '#type' => 'checkboxes',
  '#title' => t('Example checkboxes'),
  '#options' => $options,
  # New!
  '#theme' => array('multicolumn_options'),
  '#columns' => 2,
);

This will format the options for the "example" checkbox into two columns instead of one long column.

Please note that the Multi-column checkboxes radios project is a nice alternative for a more fully-featured and cross-module compatible version of this. (Except it uses tables. Ick! ;))

Comments

steve02476’s picture

Is this snippet compatible with the Webform module? (I'm using Webform 6.x-2.10.) If so, where would I add the various snippets?

I've tried the "Multi-column checkboxes radios" as per http://drupal.org/node/603900 and it worked perfectly - BUT I couldn't find a way to make it only apply to some forms and not others. Many forms on my site depend on the default behavior (non-multi-column) so I only want multi-column to affect certain forms. Is this possible with this snippet? Thanks!

colemanw’s picture

I love it when a quick Google search saves me an hour of writing my own code!
Thanks!

earthangelconsulting’s picture

awesome! i could have figured this out on my own, but you just saved me an hour :-) thanks, webchick!

dan3h’s picture

I got this working on my D7 site, with a bit of struggle. Here are the changes that I had to make to get it working:

  • Modify function theme_multicolumn_options($element) to have these 3 new lines at the beginning:
    function theme_multicolumn_options($element) {
      // Modify to dig one layer deeper.  Not sure why original code didn't need this.
      $stripped_element = array_values($element);
      $element = $stripped_element[0];
    ...
    
  • Also in the theme_multicolumn_options() function, comment out the theme() invocation, and replace with drupal_render():
    //      $output .= theme($type, $element[$keys[$current_option]]);
          $output .= drupal_render($element[$keys[$current_option]]);
    

    Without this, it was displaying only the checkboxes without their text beside them.

    With these changes, it's working great for me!

    Oh, and BTW, I'm using it with the Profile2 module, for a termreference fieldtype (lists a bunch of terms from a taxonomy for you to associate with your user account) in the "main" profile. Here is how I invoked the multicolumns, with hook_form_alter:

    /**
     * Implementation of hook_form_alter()
     */
    function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
      if ($form_id == 'user_register_form' || $form_id == 'user_profile_form' && $form['#user_category'] == 'main') {
        $form['profile_main']['field_specialties']['und']['#theme'] = array('multicolumn_options');
        $form['profile_main']['field_specialties']['und']['#multicolumn_numcols'] = 4;
      }
    }
    

    Ah, yes, that was one other change I made. I noticed that "#columns" was already in use, I think by profile2, so I renamed it to "#multicolumn_numcols". A corresponding change in theme_multicolumn_options() was to change the $total_columns line to:
    $total_columns = $element['#multicolumn_numcols'];

    So in the end, that function looked like this:

    
    function theme_multicolumn_options($element) {
      // Modify to dig one layer deeper.  Not sure why original code didn't need this.
      $stripped_element = array_values($element);
      $element = $stripped_element[0];
    
      // Initialize variables.
      $output = '';
      $total_columns = $element['#multicolumn_numcols'];
      $total_options = count($element['#options']);
      $options_per_column = ceil($total_options / $total_columns);
      $keys = array_keys($element['#options']);
      $type = $element[$keys[0]]['#type'];
    
      // Start wrapper div.
      $output .= '<div class="multicolumn-options-wrapper">';
      $current_column = 1;
      $current_option = 0;
    
      while ($current_column <= $total_columns) {
        // Start column div.
        $output .=  '<div class="multicolumn-options-column" style="width: ' . 100 / $total_columns . '%; float: left">';
    
        // Keep looping through until the maximum options per column are reached,
        // or you run out of options.
        while ($current_option < $options_per_column * $current_column &&
               $current_option < $total_options) {
    
          // Output as either check or radio button depending on the element type.
          $output .= drupal_render($element[$keys[$current_option]]);
    //      $output .= theme($type, $element[$keys[$current_option]]);
          $current_option++;
        }
    
        // End column div.
        $output .= '</div>';
        $current_column++;
      }
    
      // End wrapper div.
      $output .= '</div>';
      $output .= '<div class="clear-block"></div>';
    
      return $output;
    }
    
nagiek’s picture

Can you submit a patch for this to the maintainer?

spouilly’s picture

For Drupal 7, the hook_theme() function is slightly different (from Drupal 6 version) as well ...
see http://www.jaypan.com/blog/themeing-drupal-7-forms-including-css-and-js

For a form, we need to specify 'render element' instead of 'variables' (and to have the 'form' as value).

/**
 * Implements hook_theme().
 */
function MY_MODULE_theme() {
  return array(
    'multicolumn_options' => array(
      'render element' => 'form',
    ),
  );
}
yeaha’s picture

thanks spouilly, some variables in the theme function have to be changed then too
// $stripped_element = array_values($element);
//$element = $stripped_element[0];
$element = $element["form"];

and type has to be
//$type = $element[$keys[0]]['#type'];
$type = $element['#type'];

schiavone’s picture

In Drupal 7 the following worked for me...

Leave alterations to element as dan3h has it

 $stripped_element = array_values($element);
  $element = $stripped_element[0];

Use theme() as the original

$output .= theme($type, $element[$keys[$current_option]]);
//$output .= drupal_render($element[$keys[$current_option]]);

And be sure your variable for columns matches what you have...

// $total_columns = $element['#multicolumn_numcols'];
$total_columns = $element['#columns'];
longviet’s picture

Thanks to all for sharing. Here is my trick for my site.
function mymodule_theme() {
return array(
'multicolumn_options' => array(
'render element' => 'element', // If use agruments=>'element', It will throw notice: indefined index render element in theme()....
),
);
};

in theme_multicolumn_options(), I used $output .= drupal_render($element[$keys[$current_option]]); instead of $output .= theme(....).
Best Regards.

t9nick’s picture

I'm new(fairly) to drupal. I don't know squat about form alter this or form alter that. All I know is that I need this code on my site. I've seen this snippet, as well as many others, that explain quite well how to make things work. However, nobody seems to be thinking about me when they post their code/solutions! Please, please, please go the extra mile and identify the FILES these snippets need to be applied to :) Saying "Add the following to hook_them():" and "the following somewhere in your .module file" doesn't tell me anything about which files I need to be working on. I've spent three hours opening every file that I "think" is the right one and I'm only more confused. Now that I have crossed into hacking/modifying code I'm sure I'll pick up on the "drupal lingo" quickly. Someone please jump on here and tell me what files. Thank you :)

zilla’s picture

for now, if you want multicolumn checkboxes, try this:

download the CSS Injector module
add a rule and paste this code:

.form-type-checkboxes .form-checkboxes {
  -moz-column-count: 3;
  -moz-column-gap: 10px;
  -webkit-column-count: 3;
  -webkit-column-gap: 10px;
  column-count: 3;
  column-gap: 10px;
}

just change that 3 to however many columns you want. it will work but it will apply to all checkboxes equally

Mod74’s picture

But for Webform the above .css didn't. I used this from a post on StackExchange

.form-radios,
.form-checkboxes {
  margin-left: 5px;
}

.form-radios .form-item,
.form-checkboxes .form-item {
  font-size: 100%;
  width: 19%;         
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
  margin: 0px;
}