6.x common.inc drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0)
7.x common.inc drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0)

Assist in adding the tableDrag JavaScript behavior to a themed table.

Draggable tables should be used wherever an outline or list of sortable items needs to be arranged by an end-user. Draggable tables are very flexible and can manipulate the value of form elements placed within individual columns.

To set up a table to use drag and drop in place of weight select-lists or in place of a form that contains parent relationships, the form must be themed into a table. The table must have an id attribute set. If using theme_table(), the id may be set as such:

$output = theme('table', $header, $rows, array(
  'id' => 'my-module-table',
return $output;

In the theme function for the form, a special class must be added to each form element within the same column, "grouping" them together.

In a situation where a single weight column is being sorted in the table, the classes could be added like this (in the theme function):

$form['my_elements'][$delta]['weight']['#attributes']['class'] = "my-elements-weight";

Each row of the table must also have a class of "draggable" in order to enable the drag handles:

$row = array(...);
$rows[] = array(
  'data' => $row,
  'class' => 'draggable',

When tree relationships are present, the two additional classes 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior:

  • Rows with the 'tabledrag-leaf' class cannot have child rows.
  • Rows with the 'tabledrag-root' class cannot be nested under a parent row.

Calling drupal_add_tabledrag() would then be written as such:

drupal_add_tabledrag('my-module-table', 'order', 'sibling', 'my-elements-weight');

In a more complex case where there are several groups in one column (such as the block regions on the admin/build/block page), a separate subgroup class must also be added to differentiate the groups.

$form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = "my-elements-weight my-elements-weight-" . $region;

$group is still 'my-element-weight', and the additional $subgroup variable will be passed in as 'my-elements-weight-'. $region. This also means that you'll need to call drupal_add_tabledrag() once for every region added.

foreach ($regions as $region) {
  drupal_add_tabledrag('my-module-table', 'order', 'sibling', 'my-elements-weight', 'my-elements-weight-' . $region);

In a situation where tree relationships are present, adding multiple subgroups is not necessary, because the table will contain indentations that provide enough information about the sibling and parent relationships. See theme_menu_overview_form() for an example creating a table containing parent relationships.

Please note that this function should be called from the theme layer, such as in a .tpl.php file, theme_ function, or in a template_preprocess function, not in a form declartion. Though the same JavaScript could be added to the page using drupal_add_js() directly, this function helps keep template files clean and readable. It also prevents tabledrag.js from being added twice accidentally.


$table_id: String containing the target table's id attribute. If the table does not have an id, one will need to be set, such as <table id="my-module-table">.

$action: String describing the action to be done on the form item. Either 'match' 'depth', or 'order'. Match is typically used for parent relationships. Order is typically used to set weights on other form elements with the same group. Depth updates the target element with the current indentation.

$relationship: String describing where the $action variable should be performed. Either 'parent', 'sibling', 'group', or 'self'. Parent will only look for fields up the tree. Sibling will look for fields in the same group in rows above and below it. Self affects the dragged row itself. Group affects the dragged row, plus any children below it (the entire dragged group).

$group: A class name applied on all related form elements for this action.

$subgroup: (optional) If the group has several subgroups within it, this string should contain the class name identifying fields in the same subgroup.

$source: (optional) If the $action is 'match', this string should contain the class name identifying what field will be used as the source value when matching the value in $subgroup.

$hidden: (optional) The column containing the field elements may be entirely hidden from view dynamically when the JavaScript is loaded. Set to FALSE if the column should not be hidden.

$limit: (optional) Limit the maximum amount of parenting in this table.

See also



9 calls to drupal_add_tabledrag()
block-admin-display-form.tpl.php in modules/block/block-admin-display-form.tpl.php
block-admin-display-form.tpl.php Default theme implementation to configure blocks.
theme_book_admin_table in modules/book/book.admin.inc
Theme function for the book administration page form.
theme_filter_admin_order in modules/filter/filter.admin.inc
Theme filter order configuration form.
theme_menu_overview_form in modules/menu/menu.admin.inc
Theme the menu overview form into a table.
theme_profile_admin_overview in modules/profile/profile.admin.inc
Theme the profile field overview into a drag and drop enabled table.

... See full list


includes/common.inc, line 2503
Common functions that many Drupal modules will need to reference.


function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0) {
  static $js_added = FALSE;
  if (!$js_added) {
    drupal_add_js('misc/tabledrag.js', 'core');
    $js_added = TRUE;

  // If a subgroup or source isn't set, assume it is the same as the group.
  $target = isset($subgroup) ? $subgroup : $group;
  $source = isset($source) ? $source : $target;
  $settings['tableDrag'][$table_id][$group][] = array(
    'target' => $target,
    'source' => $source,
    'relationship' => $relationship,
    'action' => $action,
    'hidden' => $hidden,
    'limit' => $limit,
  drupal_add_js($settings, 'setting');


ifux’s picture

the class as declared above doesn't work!

has to be an Array as it seems!

$row = array(...);
$rows[] = array(
  'data' => $row,
  'class' => array('draggable'),
TR’s picture

@iflux: Please note that the instructions for posting a new comment say "Buggy or inaccurate documentation? Please file an issue instead of commenting here. "

Regardless, you're wrong about class having to be an array(). This documentation page is for the Drupal 6 version of drupal_add_tabledrag(). In Drupal 6, the class should be a string, and the above documentation is correct when it says:

$rows[] = array(
  'data' => $row,
  'class' => 'draggable',

It's only in the Drupal 7 version of this function, which is documented at http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_ad..., where class should be an array, and the Drupal 7 documentation does show that.

So, the documentation about 'class' is correct on this page.

voj’s picture

Where do you get the variable $form?

is it from the rows or is it in the form api?

berliner’s picture

I just stumbled upon something I haven't been aware of before. When you place the weight element in the first column of the table then the handle is not displayed.
As I understand the logic, the weight column will be hidden using display:none and the drag handle is added to the first column. So if the weight column is the first one in the table, then the effect is, that this column is hidden and so is the handle inside that column.

Edge case, but still might save someone some trouble.

msound’s picture

For some bizarre reason, the guy who added the table columns in my project decided that weight column should be int(10) unsigned. That is, only positive values are allowed to be stored.

The weight values are typically from -50 to +50. So, all the weight values were being stored as 0 in the database, making me think that tabledrag feature is not working.

It is very rare that someone would choose int(10) unsigned for the weight column, but if you do and you are breaking your head, maybe this comment would help!