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)

Assists 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 follows:

$output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => 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'] = array('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' => array('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/structure/block page), a separate subgroup class must also be added to differentiate the groups.

$form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = array('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.

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 declaration. 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



16 calls to drupal_add_tabledrag()
block-admin-display-form.tpl.php in modules/block/block-admin-display-form.tpl.php
Default theme implementation to configure blocks.
theme_book_admin_table in modules/book/book.admin.inc
Returns HTML for a book administration form.
theme_field_multiple_value_form in modules/field/field.form.inc
Returns HTML for an individual form element.
theme_file_widget_multiple in modules/file/file.field.inc
Returns HTML for a group of file upload widgets.
theme_filter_admin_format_filter_order in modules/filter/filter.admin.inc
Returns HTML for a text format's filter order form.

... See full list

2 string references to 'drupal_add_tabledrag'
field_ui_display_overview_form in modules/field_ui/field_ui.admin.inc
Form constructor for the field display settings for a given view mode.
field_ui_field_overview_form in modules/field_ui/field_ui.admin.inc
Form constructor for the 'Manage fields' form of a bundle.


includes/common.inc, line 5029
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) {
  $js_added = &drupal_static(__FUNCTION__, FALSE);
  if (!$js_added) {
    // Add the table drag JavaScript to the page before the module JavaScript
    // to ensure that table drag behaviors are registered before any module
    // uses it.
    drupal_add_library('system', 'jquery.cookie');
    drupal_add_js('misc/tabledrag.js', array('weight' => -1));
    $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');


rosell.dk’s picture

It would be nice with a link to an example. Meanwhile, I recommend looking at the form: "filter_admin_overview" in filter/filter.admin.inc. Its very simple. Also search for "filter_admin_overview" in filter/filter.module to see how to provide the hook_theme / hook_menu

fenda’s picture

I've put a small bounty out for someone who can provide a decent example of subgroup usage with this function. For more details see here:

duckzland’s picture

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

doesnt work but

drupal_add_tabledrag('my-module-table', 'order', 'sibling', $group = 'my-elements-weight', NULL, NULL, FALSE);

does work

JoeRobertsPhotography’s picture

Thanks so much this totally fixed my issue. It started working after this. The drag and drop handles were just highlight yellow and sliding but never stopping and this fixed it. Any one else having issue please use this as well.

Now if anyone can help me save the results of the drupal_add_tabledrag inside of block that would be awesome. I am trying to loop through the results inside of hook_block_save() but that isn't working.

The example that I found uses the form submit button not sure how to do something similar to this inside a block.

function tabledrag_example_simple_form_submit($form, &$form_state) {
  // Because the form elements were keyed with the item ids from the database,
  // we can simply iterate through the submitted values.
  foreach ($form_state['values']['example_items'] as $id => $item) {
      "UPDATE {tabledrag_example} SET weight = :weight WHERE id = :id",
      array(':weight' => $item['weight'], ':id' => $id)

If anyone can help it is much appreciated.

JoeRobertsPhotography’s picture

I figured it out. You can see how here http://drupal.org/node/1683162.

hgneng’s picture

I've tried to roll out my first tabledrag form. It wasted me a lot of time because I need to understand how theme hook works. To save other's time, I recommend taking a look at this project: http://drupal.org/sandbox/jthorson/1440578

It's not a stable project at the time I make this post, but the simple example works fine.

After checkout and install the tabledrag_example, open examples/tabledrag_example_simple to see the example page. The code of the example is in tabledrag_example_simple_form.inc. Remember we need to register a theme in .module file.


danielb’s picture

That project has no files/code

holtzermann17’s picture

Just downloaded and looking through it now... hm, yes, it's a very good example!

(Thanks jthorson.)

develCuy’s picture

Check the source code here:

I strongly recommend you to install the examples module, test first how it works, play a bit with the source code and then read it carefully, there is a lot of pretty useful stuff to learn about the fiery core's tabledrag.

mathieso’s picture

Nice example. Thnx. A w00t to you.

drm’s picture

For a while, my form was not submitting - wasn't going to the submission handler. I found that after normally declaring the submit element, you need to include something like this in the theme function after theming the draggable table:

  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'draggabledemo-sort')));
  $output .= drupal_render_children($form);

The drupal_render_children call catches the submit button.

shabana.navas’s picture

Please make sure there is a column before your weight column, otherwise the drag handle and weight column will show up together.

plong0’s picture

If you'd like to provide custom javascript handlers when a row is dragged or dropped, you can do something like this:

  Drupal.tableDrag['myDragTableID'].onDrag = function(){ // it's dragging! };
  Drupal.tableDrag['myDragTableID'].onDrop = function(){ // it's dropped! };

I put it in a jQuery document ready function... There are likely several other places you could put it depending on specific needs.

mparker17’s picture

The tabledrag javascript puts the drag handles inside the first column, then hides the weight column.

This means that, if you put the row weight in the first column, it will add the drag handles to that column, then hide it.