function theme_table

You are here

7 theme.inc theme_table($variables)
4.6 theme.inc theme_table($header, $rows, $attributes = NULL)
4.7 theme.inc theme_table($header, $rows, $attributes = array(), $caption = NULL)
5 theme.inc theme_table($header, $rows, $attributes = array(), $caption = NULL)
6 theme.inc theme_table($header, $rows, $attributes = array(), $caption = NULL)
8 theme.inc theme_table($variables)

Returns HTML for a table.

Parameters

$variables: An associative array containing:

  • header: An array containing the table headers. Each element of the array can be either a localized string or an associative array with the following keys:

    • "data": The localized title of the table column.
    • "field": The database field represented in the table column (required if user is to be able to sort on this column).
    • "sort": A default sort order for this column ("asc" or "desc"). Only one column should be given a default sort order because table sorting only applies to one column at a time.
    • Any HTML attributes, such as "colspan", to apply to the column header cell.
  • rows: An array of table rows. Every row is an array of cells, or an associative array with the following keys:

    • "data": an array of cells
    • Any HTML attributes, such as "class", to apply to the table row.
    • "no_striping": a boolean indicating that the row should receive no 'even / odd' styling. Defaults to FALSE.

    Each cell can be either a string or an associative array with the following keys:

    • "data": The string to display in the table cell.
    • "header": Indicates this cell is a header.
    • Any HTML attributes, such as "colspan", to apply to the table cell.

    Here's an example for $rows:

    $rows = array(
      // Simple row
      array(
        'Cell 1', 'Cell 2', 'Cell 3'
      ),
      // Row with attributes on the row and some of its cells.
      array(
        'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
      )
    );
    
  • attributes: An array of HTML attributes to apply to the table tag.
  • caption: A localized string to use for the <caption> tag.
  • colgroups: An array of column groups. Each element of the array can be either:

    • An array of columns, each of which is an associative array of HTML attributes applied to the COL element.
    • An array of attributes applied to the COLGROUP element, which must include a "data" attribute. To add attributes to COL elements, set the "data" attribute with an array of columns, each of which is an associative array of HTML attributes.

    Here's an example for $colgroup:

    $colgroup = array(
      // COLGROUP with one COL element.
      array(
        array(
          'class' => array('funky'), // Attribute for the COL element.
        ),
      ),
      // Colgroup with attributes and inner COL elements.
      array(
        'data' => array(
          array(
            'class' => array('funky'), // Attribute for the COL element.
          ),
        ),
        'class' => array('jazzy'), // Attribute for the COLGROUP element.
      ),
    );
    

These optional tags are used to group and set properties on columns within a table. For example, one may easily group three columns and apply same background style to all.

  • sticky: Use a "sticky" table header.
  • empty: The message to display in an extra row if table does not have any rows.

Related topics

46 theme calls to theme_table()
aggregator_view in modules/aggregator/aggregator.admin.inc
Displays the aggregator administration page.
book_admin_overview in modules/book/book.admin.inc
Returns an administrative overview of all books.
field_ui_fields_list in modules/field_ui/field_ui.admin.inc
Menu callback; lists all defined fields for quick reference.
locale_date_format_language_overview_page in modules/locale/locale.admin.inc
Display edit date format links for each language.
locale_translate_overview_screen in modules/locale/locale.admin.inc
Overview screen for translations.

... See full list

File

includes/theme.inc, line 1895
The theme system, which controls the output of Drupal.

Code

function theme_table($variables) {
  $header = $variables['header'];
  $rows = $variables['rows'];
  $attributes = $variables['attributes'];
  $caption = $variables['caption'];
  $colgroups = $variables['colgroups'];
  $sticky = $variables['sticky'];
  $empty = $variables['empty'];

  // Add sticky headers, if applicable.
  if (count($header) && $sticky) {
    drupal_add_js('misc/tableheader.js');
    // Add 'sticky-enabled' class to the table to identify it for JS.
    // This is needed to target tables constructed by this function.
    $attributes['class'][] = 'sticky-enabled';
  }

  $output = '<table' . drupal_attributes($attributes) . ">\n";

  if (isset($caption)) {
    $output .= '<caption>' . $caption . "</caption>\n";
  }

  // Format the table columns:
  if (count($colgroups)) {
    foreach ($colgroups as $number => $colgroup) {
      $attributes = array();

      // Check if we're dealing with a simple or complex column
      if (isset($colgroup['data'])) {
        foreach ($colgroup as $key => $value) {
          if ($key == 'data') {
            $cols = $value;
          }
          else {
            $attributes[$key] = $value;
          }
        }
      }
      else {
        $cols = $colgroup;
      }

      // Build colgroup
      if (is_array($cols) && count($cols)) {
        $output .= ' <colgroup' . drupal_attributes($attributes) . '>';
        $i = 0;
        foreach ($cols as $col) {
          $output .= ' <col' . drupal_attributes($col) . ' />';
        }
        $output .= " </colgroup>\n";
      }
      else {
        $output .= ' <colgroup' . drupal_attributes($attributes) . " />\n";
      }
    }
  }

  // Add the 'empty' row message if available.
  if (!count($rows) && $empty) {
    $header_count = 0;
    foreach ($header as $header_cell) {
      if (is_array($header_cell)) {
        $header_count += isset($header_cell['colspan']) ? $header_cell['colspan'] : 1;
      }
      else {
        $header_count++;
      }
    }
    $rows[] = array(array(
      'data' => $empty,
      'colspan' => $header_count,
      'class' => array('empty', 'message'),
    ));
  }

  // Format the table header:
  if (count($header)) {
    $ts = tablesort_init($header);
    // HTML requires that the thead tag has tr tags in it followed by tbody
    // tags. Using ternary operator to check and see if we have any rows.
    $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
    foreach ($header as $cell) {
      $cell = tablesort_header($cell, $header, $ts);
      $output .= _theme_table_cell($cell, TRUE);
    }
    // Using ternary operator to close the tags based on whether or not there are rows
    $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
  }
  else {
    $ts = array();
  }

  // Format the table rows:
  if (count($rows)) {
    $output .= "<tbody>\n";
    $flip = array(
      'even' => 'odd',
      'odd' => 'even',
    );
    $class = 'even';
    foreach ($rows as $number => $row) {
      // Check if we're dealing with a simple or complex row
      if (isset($row['data'])) {
        $cells = $row['data'];
        $no_striping = isset($row['no_striping']) ? $row['no_striping'] : FALSE;

        // Set the attributes array and exclude 'data' and 'no_striping'.
        $attributes = $row;
        unset($attributes['data']);
        unset($attributes['no_striping']);
      }
      else {
        $cells = $row;
        $attributes = array();
        $no_striping = FALSE;
      }
      if (count($cells)) {
        // Add odd/even class
        if (!$no_striping) {
          $class = $flip[$class];
          $attributes['class'][] = $class;
        }

        // Build row
        $output .= ' <tr' . drupal_attributes($attributes) . '>';
        $i = 0;
        foreach ($cells as $cell) {
          $cell = tablesort_cell($cell, $header, $ts, $i++);
          $output .= _theme_table_cell($cell);
        }
        $output .= " </tr>\n";
      }
    }
    $output .= "</tbody>\n";
  }

  $output .= "</table>\n";
  return $output;
}

Comments

The final call to the theme function needs to pass in an associative array as well which appears to have been changed for D7.

$output = theme('table', array('header' => $header, 'rows' => $rows));

Please add support for multiple rows in the table header or document how to achieve this with the existing function.

If anyone has any workaround for multiple rows in the table header, please do share. Thanks.

I am helping out on reworking the theme_table for d8 and was wondering what use-cases you would like this feature for? This will be an API changer so just want to do my due diligence.

This will be a great feature to have so thanks for your work on this! I am working on a project now that requires a detailed table listing clients with several "sections" within that table for each client category, and I want the table to be continuous with same look and width.

One feature I'd like is to be able to disable sticky headers and use a plain table (eg. when creating a table to embed in an email)

Set 'sticky' to FALSE in something like:

  $table = array('header' => $header, 'rows' => $rows, 'attributes' => $attributes, 'sticky' => FALSE);
  $html .= theme_table($table);

We use text-overflow to cut down long strings of text and the lack of "title" attributes makes our life difficult for the hover state...

The workaraund I used was not to create a 'header' key but include the header in the 'rows' section.

This seems to be undocumented as of yet, but the internal api function _theme_table_cell has a special condition when the cell (each element in a row array) is defined using the 'data' variant. When using this definition, if you specify an array instead of a string, it will drupal_render the contents.

For instance :

<?php
foreach ($objects as $key => $object) {
   
// this is the well documented format
   
$options[$key]['title'] = filter_xss($object->title);

   

// this is the not well known format
   
$options[$key]['awesomeness'] =  array(
     
'data' => array(
       
'#type' => 'select',
       
'#options' => array(
         
'0' => t('boring'),
         
'1' => t('Awesome!'),
         
'11' => t('TURNED UP TO ELEVEN!!11!')
        ),
       
'#default_value' => $object->awesomeness,
     ));
}

$form['things'] = array(
   
'#type' => 'tableselect',
   
'#header' => array('title' => t('Title'), 'awesomeness' => t('AWESOMENESS')),
   
'#options' => $options,
   
'#empty' => t('No items available'),
  );
?>

This means you no longer have to have your own '#theme' function on the form to render the elements in the right cells.
This in turn means that you could end up deleting hundreds of lines of complex code that confuses themers and coders alike.

They can be renderable arrays but this way they are not processed by the FAPI because the form elements are in a property (starts with '#') and they are not children of the tableselect element (tableselect itself is not a container). (So AJAX won't work out of the box for example and they won't be in the values array for form submissions - only in input.)

trying to do a settings table with theme_table and system_settings_form seems not to work for this very reason.

This code does work. it gives a fetal error.

theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array('class' => 'mytable')
));

Fatal error: [] operator not supported for strings in C:\wamp\www\damjd\includes\theme.inc on line 1633

But the code below works.

theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array('width' => '100%')
));

You can use class attribute as an array:

<?php
theme
('table', array(
 
'header' => $header,
 
'rows' => $options,
 
'attributes' => array('class' => array('mytable'))
));
?>

and it's works fine.

... for fixing this in documentation: http://drupal.org/node/1135020 -- Need standard way to document the format of attributes in theme functions

Sometimes sticky headers will crash IE8. I haven't been able to reproduce this, but just in case anyone else is trying to figure out IE8 crashes on validated HTML and using theme_table properly, it's probably sticky headers.

I just ran into this myself too. It's combination of having breadcrumb hidden and sticky headers enabled. I was able to get sticky headers working on IE8 after adding one element with class="element-invisible" to the place where breadcrumb normally was (theme based on bartik).

I am trying to make Drupal assign column names as "id" to table headers and "headers" to table datas.
I want to add this option to templates.php
I think "theme_table" and "_theme_table_cell" functions" can do it, but I don't know how, and the second is not overridable.

I wrote this foreach loop

<?php
   
foreach($header as $headings){
    if (
is_array($headings)){
   
$headers=$headings['data'] . " ";
    echo
$headers;
        }
    else
    {
   
$headers=$headings . " ";
    echo
$headers;
        }
    };
?>

which shows all of the column titles
As I clear the cache a message alerts that "data" is an undefined index in overridden function "theme_table".

Further I don't know how to assign the results to single cells.

So as you say, my form elements within a table cell don't have ajax attached to them. Could you point me in the right direction, where to start attaching it manually?

Thanks muchly

No even attaching it manually wouldn't work! here is the solution http://stackoverflow.com/questions/1981781/embedding-an-ahah-form-elemen...

Not finding anything on this, and thinking I'm missing a #type. How do I make this work:

$output['legend'] =  array(
            '#theme' => 'table',
            '#prefix' => '<div style="width: 250px"><h3>Legend</h3>',
            '#suffix' => '</div>',
            'content' => array('header' => array(), 'rows' => $legend)
          );

Use this format instead

<?php
$table_element
= array(
   
'#theme' => 'table',
   
'#header' => $header,
   
'#rows' => $rows,
   
'#empty' =>t('Your table is empty'),
);

drupal_render($table_element);
?>

I get warnings/errors if I don't specifically pass attributes, caption, colgroups, sticky, empty. This is very annoying.

One thing I'd like to note in regards to the class attribute and sticky...

If you are setting an attribute 'class', you must do it like this:

'attributes' => array('class' => array('table_dfs')),

...so 'class' is an array of class names (not a string).

I was not doing this, and when I set sticky => TRUE, my page was bombing out because it was trying to add another element to the class array which was a string.

FYI, default sort order does not work in D7, and the header-related documentation on this page appears to be incorrect in any case.

re: default sort order, see this bug: http://drupal.org/node/109493

For sorting, the header items need these keys: data, type & specifier.

For an entity's native properties, that's where it ends. But for fields you've added to the entity, the specifier should be an array with 'field' and 'column' keys. Here is an example:

$header_array = array(
      'name' => array('data' => 'Username', 'type' => 'property', 'specifier' => 'name', 'sort' => 'desc'),
      'location' => array('data' => 'Location', 'type' => 'field', 'specifier' => array('field' => 'field_city', 'column' => 'value'), 'sort' => 'desc'),
      'created' => array('data' => 'Date Joined', 'type' => 'property', 'specifier' => 'created', 'sort' => 'desc'),
      'motto' => array('data' => 'Motto'),
      'action' => array('data' => 'Last Action', 'type' => 'property', 'specifier' => 'access', 'sort' => 'desc'),
);

Note that the sort value has no effect because of the above-mentioned bug.

Then again, it may be that these docs were written to encourage coding in a way that's consistent with how the sort feature will work when it's fixed?

Posted by greggles on September 21, 2009 at 5:30pm

From berdir:

TableSort now works as an Extender, so you need to convert your query
to a dynamic db_select() query.

You then need to extend it with "->extend('TableSort')" and add the
table header by calling "->orderByHeader($header)".

Some Links:

An example from core: http://api.drupal.org/api/function/poll_votes/7
Extender doc page: http://drupal.org/node/508796

The example above can be extended as:

<?php
$rows
= array(
     
// Simple row
     
array(
        array(
'data' => 'Cell 1', class=>'col_class_name'), 'Cell 2', 'Cell 3'
     
),
     
// Row with attributes on the row and some of its cells.
     
array(
       
'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
      )
    );
?>

Adding td classes with:

array('data' => 'Cell 1', class=>'col_class_name')

This snippet helps to visualize the whole nesting scheme in a super simple manner. Additionally, it really works very fine in everyday life (hint: in a looping control structure you just have to substitute the first line with '$arr_rows[] =' ).

PHP

<?php
//Main arrays container array:
$arr_rows = array(
 
//Contains the rows, is a numerical array (as default): 0, 1, 2 ... :
 
array( 
   
//'data' contains cell data as numerical arrays: 0, 1, 2 ... :
   
'data' => array(
     
//Cell_1: 
     
array('data' => 'R#1_Cell#1', 'class' => 'css-class-of-cell1'),
     
//Cell_2:
     
array('data' => 'R#1_Cell#2', 'class' => 'css-class-of-cell2'),
     
//Cell_3:
     
array('data' => 'R#1_Cell#3', 'class' => 'css-class-of-cell3'),
    ),
   
//Row attributes, e.g. 'class'
   
'class' => array('css-class-of-row-1'),
  ),
 
//Contains the rows, is a numerical array (as default): 0, 1, 2 ... :
 
array( 
   
//'data' contains cell data as numerical arrays: 0, 1, 2 ... :
   
'data' => array(
     
//Cell_1: 
     
array('data' => 'R#2_Cell#1', 'class' => 'css-class-of-cell1'),
     
//Cell_2:
     
array('data' => 'R#2_Cell#2', 'class' => 'css-class-of-cell2'),
     
//Cell_3:
     
array('data' => 'R#2_Cell#3', 'class' => 'css-class-of-cell3'),
    ),
   
//Row attributes, e.g. 'class'
   
'class' => array('css-class-of-row-2'),
  ),
);
?>

print_R output

... (Array, 2 elements)

    0 (Array, 2 elements)
        data (Array, 3 elements)
            0 (Array, 2 elements)
                data (String, 10 characters ) R#1_Cell#1
                class (String, 18 characters ) css-class-of-cell1
            1 (Array, 2 elements)
                data (String, 10 characters ) R#1_Cell#2
                class (String, 18 characters ) css-class-of-cell2
            2 (Array, 2 elements)
                data (String, 10 characters ) R#1_Cell#3
                class (String, 18 characters ) css-class-of-cell3
        class (Array, 1 element)
            0 (String, 18 characters ) css-class-of-row-1
    1 (Array, 2 elements)
        data (Array, 3 elements)
            0 (Array, 2 elements)
                data (String, 10 characters ) R#2_Cell#1
                class (String, 18 characters ) css-class-of-cell1
            1 (Array, 2 elements)
                data (String, 10 characters ) R#2_Cell#2
                class (String, 18 characters ) css-class-of-cell2
            2 (Array, 2 elements)
                data (String, 10 characters ) R#2_Cell#3
                class (String, 18 characters ) css-class-of-cell3
        class (Array, 1 element)
            0 (String, 18 characters ) css-class-of-row-2

$output = theme('table', array(
'header' => $header,
'rows' => $rows,
'sticky' => TRUE, // or FALSE
)
);

I have used exactly same code..
$output = theme('table', array(
'header' => $header,
'rows' => $row,
'attributes' => array('width' => '100%')
));
dpm($output);
}
drupal_render($output);
}

dpm($output) give me bunchof table tags(HTML), but I can not able to reder those elements in the page..what do i do to render the elements

I don't know what the rest of your code looks like, but try just doing:
return $output;

The no striping option has an issue and is being worked on here: http://drupal.org/node/935066

I'd need to add a title attribute to each td that would be the actual content of the td.. Any hints?

If you want the user to be able to sort on a column that has numbers, but the field was created as a string (text), you can get proper numerical sorting by converting the value in the query using the SQL functions CAST() or CONVERT() in a query addExpression function as in:

$query->addExpression('CAST(table.field_string_value as DECIMAL(3,1))', 'string_now_a_number');

this is like the most common way for me to use theme_table, i though it might save somebody else's time


$headers = array(
'user' => array('data' => t('Username'), 'field' => 'u.name'),
'status' => array('data' => t('Status'), 'field' => 'u.status'),
'ops' => array('date' => t('Operations'))

);

$query = db_select('users', 'u')
->extend('TableSort');

$result = $query->fields('u', array('uid', 'name', 'status'))
->orderByHeader($headers)
->execute();

$rows = array();
foreach ($result as $invited_users) {
$rows[$invited_users->uid] = array(
'username' => l($invited_users->name, 'user/' . $invited_users->uid),
'status' => $invited_users->status,
'ops' => l('delete', 'delete')
);
}

$form['invited_users'] = array(
'#markup' => theme('table', array(
'header' => $headers,
'rows' => $rows,
'attributes' => array('class' => array('invited_users')),
'#empty' =>t('No invited users yet'),
))
);