If you are familiar with the Drupal forms API you know the concept of form elements as the basic building blocks of any form. Drupal provides several types of elements, some rather straightforward (#type textfield), some more advanced (#type password_confirm).
It is also possible to define custom form elements that enable you to implement complex widgets with ease.
At the moment, the focus of Elements is the tableselect widget. It provides a clean, easy way to create tables with one checkbox per row, multi-row selection and a select all checkbox, without making your code a complicated mess.
You can use the following code to create an example.module which you can use to experiment with the examples in this guide.
The comboselect element has not yet been included in Elements. You can follow its development at #268424.
The comboselect written by John Morahan gives you a select type element with an 'Other' option. When the user chooses this option; a textfield appears.
| Attachment | Size |
|---|---|
| comboselect_other_wording.JPG | 15.45 KB |
The form element tableselect provides a clean, easy way to create tables with one checkbox per row, multi-row selection and a select all checkbox, without making your code a complicated mess:

Update: This book is about the tableselect element provided by the Elements module for Drupal 5 and 6. See A new form element in Drupal core for a HOWTO on the new Drupal 7 form element.
The basic properties of the tableselect element are #header which accepts an array of key => value pairs to generate the header from and #options, which receives an array of rows.
The basic structure looks like the following:
The tableselect element relies on the keys to match columns with the appropriate header. The element will behave like the #checkboxes type. You'll get an array of values in your submit function, with the checked ones set to nonzero so you can array_filter them to get the checked rows.
$header = array(
'title' => t('Title'),
'author' => t('Author'),
);
$query = "SELECT n.nid, n.title, u.name FROM {node} n INNER JOIN {users} u ON n.uid = u.uid";
$result = pager_query(db_rewrite_sql($query));
while($partial_node = db_fetch_object($result)) {
$options[$partial_node->nid] = array(
'title' => check_plain($partial_node->title),
'author' => check_plain($partial_node->name),
);
}
if (!empty($options)) {
$form['nodes'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
);
$form['pager'] = array('#value' => theme('pager'));
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
return $form;
}
Which results in:

The tableselect element supports use with tablesort_sql. Simply modify the header to the format described in theme_table.
$header = array(
'title' => array('field' => 'n.title', 'data' => t('Title')),
'author' => array('field' => 'u.name', 'data' => t('Author')),
);
$query = "SELECT n.nid, n.title, u.name FROM {node} n INNER JOIN {users} u ON n.uid = u.uid". tablesort_sql($header);
$result = pager_query(db_rewrite_sql($query));
while($partial_node = db_fetch_object($result)) {
$options[$partial_node->nid] = array(
'title' => check_plain($partial_node->title),
'author' => theme('username', $partial_node),
);
}
if (!empty($options)) {
$form['nodes'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
);
$form['pager'] = array('#value' => theme('pager'));
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
return $form;
}
