diff --git a/core/includes/common.inc b/core/includes/common.inc
index 12ccffd..d065fd4 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2930,6 +2930,71 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro
 }
 
 /**
+ * Assists in attaching the tableDrag JavaScript behavior to a themed table.
+ *
+ *
+ * @param $element
+ *   A form element to attach the tableDrag.
+ *
+ * @param array $options
+ *   An associative array containing:
+ *   - 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 classname 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.
+ */
+function drupal_attach_tabledrag(&$element, array $options) {
+  // Add default values to elements.
+  $options = $options + array(
+      'subgroup' => NULL,
+      'source' => NULL,
+      'hidden' => TRUE,
+      'limit' => 0
+    );
+
+  $group = $options['group'];
+
+  $tabledrag_id = &drupal_static(__FUNCTION__ . '_setting', FALSE);
+  $tabledrag_id = (!isset($tabledrag_id)) ? 0 : $tabledrag_id + 1;
+
+  // If a subgroup or source isn't set, assume it is the same as the group.
+  $target = isset($options['subgroup']) ? $options['subgroup'] : $group;
+  $source = isset($options['source']) ? $options['source'] : $target;
+  $settings['tableDrag'][$options['table_id']][$group][$tabledrag_id] = array(
+    'target' => $target,
+    'source' => $source,
+    'relationship' => $options['relationship'],
+    'action' => $options['action'],
+    'hidden' => $options['hidden'],
+    'limit' => $options['limit'],
+  );
+
+  $element['#attached']['library'][] = array('system', 'drupal.tabledrag');
+  $element['#attached']['js'][] = array('data' => $settings, 'type' => 'setting');
+}
+
+/**
  * Deletes old cached JavaScript files and variables.
  */
 function drupal_clear_js_cache() {
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 726503e..988c293 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1448,9 +1448,10 @@ function drupal_pre_render_table(array $element) {
   // HTML ID as first callback argument and attach the behavior.
   if (!empty($element['#tabledrag']) && isset($element['#attributes']['id'])) {
     foreach ($element['#tabledrag'] as &$args) {
-      array_unshift($args, $element['#attributes']['id']);
+      $args['table_id'] = $element['#attributes']['id'];
+      drupal_attach_tabledrag($element, $args);
     }
-    $element['#attached']['drupal_add_tabledrag'] = $element['#tabledrag'];
+
   }
 
   return $element;
diff --git a/core/lib/Drupal/Core/Config/Entity/DraggableListController.php b/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
index 5370be5..2cb3f43 100644
--- a/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
+++ b/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
@@ -101,7 +101,11 @@ public function buildForm(array $form, array &$form_state) {
       '#header' => $this->buildHeader(),
       '#empty' => t('There is no @label yet.', array('@label' => $this->entityInfo['label'])),
       '#tabledrag' => array(
-        array('order', 'sibling', 'weight'),
+        array(
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => 'weight',
+        ),
       ),
     );
 
diff --git a/core/modules/block/lib/Drupal/block/BlockListController.php b/core/modules/block/lib/Drupal/block/BlockListController.php
index 9524b0d..6db9219 100644
--- a/core/modules/block/lib/Drupal/block/BlockListController.php
+++ b/core/modules/block/lib/Drupal/block/BlockListController.php
@@ -200,18 +200,17 @@ public function buildForm(array $form, array &$form_state) {
     // Loop over each region and build blocks.
     foreach ($block_regions_with_disabled as $region => $title) {
       $form['blocks']['#tabledrag'][] = array(
-        'match',
-        'sibling',
-        'block-region-select',
-        'block-region-' . $region,
-        NULL,
-        FALSE,
+        'action' => 'match',
+        'relationship' => 'sibling',
+        'group' => 'block-region-select',
+        'subgroup' => 'block-region-' . $region,
+        'hidden' => FALSE,
       );
       $form['blocks']['#tabledrag'][] = array(
-        'order',
-        'sibling',
-        'block-weight',
-        'block-weight-' . $region,
+        'action' => 'order',
+        'relationship' => 'sibling',
+        'group' => 'block-weight',
+        'subgroup' => 'block-weight-' . $region,
       );
 
       $form['blocks'][$region] = array(
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index 0ffefc5..d207490 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -22,8 +22,25 @@
 function theme_book_admin_table($variables) {
   $form = $variables['form'];
 
-  drupal_add_tabledrag('book-outline', 'match', 'parent', 'book-plid', 'book-plid', 'book-mlid', TRUE, MENU_MAX_DEPTH - 2);
-  drupal_add_tabledrag('book-outline', 'order', 'sibling', 'book-weight');
+  $match_options = array(
+    'table_id' => 'book-outline',
+    'action' => 'match',
+    'relationship' => 'parent',
+    'group' => 'book-plid',
+    'subgroup' => 'book-plid',
+    'source' => 'book-mlid',
+    'hidden' => TRUE,
+    'limit' => MENU_MAX_DEPTH - 2,
+  );
+  $order_options = array(
+    'table_id' => 'book-outline',
+    'action' => 'order',
+    'relationship' => 'sibling',
+    'group' => 'book-weight',
+  );
+
+  drupal_attach_tabledrag($form, $match_options);
+  drupal_attach_tabledrag($form, $order_options);
 
   $header = array(t('Title'), t('Weight'), t('Parent'), t('Operations'));
 
diff --git a/core/modules/field/field.form.inc b/core/modules/field/field.form.inc
index ebdc3d7..382beda 100644
--- a/core/modules/field/field.form.inc
+++ b/core/modules/field/field.form.inc
@@ -78,13 +78,19 @@ function theme_field_multiple_value_form($variables) {
         'class' => array('field-multiple-table'),
       ),
     );
+
+    $options = array(
+      'table_id' => $table_id,
+      'action' => 'order',
+      'relationship' => 'sibling',
+      'group' => $order_class,
+    );
+    drupal_attach_tabledrag($table, $options);
     $output = '<div class="form-item">';
     $output .= drupal_render($table);
     $output .= $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
     $output .= '<div class="clearfix">' . drupal_render($add_more_button) . '</div>';
     $output .= '</div>';
-
-    drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
   }
   else {
     foreach (element_children($element) as $key) {
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
index 31a08ec..f6cd9b6 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php
@@ -196,8 +196,22 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL,
     $form['#attached']['library'][] = array('field_ui', 'drupal.field_ui');
 
     // Add tabledrag behavior.
-    $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'order', 'sibling', 'field-weight');
-    $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name');
+    $order_options = array(
+      'table_id' => 'field-display-overview',
+      'action' => 'order',
+      'relationship' => 'sibling',
+      'group' => 'field-weight',
+    );
+    $match_options = array(
+      'table_id' => 'field-display-overview',
+      'action' => 'match',
+      'relationship' => 'parent',
+      'group' => 'field-parent',
+      'subgroup' => 'field-parent',
+      'source' => 'field-name',
+    );
+    drupal_attach_tabledrag($form, $order_options);
+    drupal_attach_tabledrag($form, $match_options);
 
     return $form;
   }
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index a42bd78..85959aa 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -413,7 +413,6 @@ function theme_file_widget_multiple($variables) {
     );
   }
 
-  drupal_add_tabledrag($table_id, 'order', 'sibling', $weight_class);
 
   $build = array(
     '#theme' => 'table',
@@ -424,6 +423,14 @@ function theme_file_widget_multiple($variables) {
     ),
   );
 
+  $options = array(
+    'table_id' => $table_id,
+    'action' => 'order',
+    'relationship' => 'sibling',
+    'group' => $weight_class,
+  );
+  drupal_attach_tabledrag($build, $options);
+
   $output = empty($rows) ? '' : drupal_render($build);
   $output .= drupal_render_children($element);
   return $output;
diff --git a/core/modules/filter/lib/Drupal/filter/FilterFormatFormControllerBase.php b/core/modules/filter/lib/Drupal/filter/FilterFormatFormControllerBase.php
index 002c1d5..456a3f1 100644
--- a/core/modules/filter/lib/Drupal/filter/FilterFormatFormControllerBase.php
+++ b/core/modules/filter/lib/Drupal/filter/FilterFormatFormControllerBase.php
@@ -137,7 +137,11 @@ public function form(array $form, array &$form_state) {
       '#attributes' => array('id' => 'filter-order'),
       '#title' => t('Filter processing order'),
       '#tabledrag' => array(
-        array('order', 'sibling', 'filter-order-weight'),
+        array(
+         'action' => 'order',
+         'relationship' => 'sibling',
+         'group' => 'filter-order-weight',
+        ),
       ),
       '#tree' => FALSE,
       '#input' => FALSE,
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index b8f9c96..d3246f6 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -59,7 +59,13 @@ function theme_image_style_effects($variables) {
     '#rows' => $rows,
     '#attributes' => array('id' => 'image-style-effects'),
   );
-  drupal_add_tabledrag('image-style-effects', 'order', 'sibling', 'image-effect-order-weight');
+  $options = array(
+    'table_id' => 'image-style-effects',
+    'action' => 'order',
+    'relationship' => 'sibling',
+    'group' => 'image-effect-order-weight',
+  );
+  drupal_attach_tabledrag($table, $options);
   return drupal_render($table);
 }
 
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index f9443c7..97ed1c2 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -219,12 +219,18 @@ function theme_language_negotiation_configure_form($variables) {
       '#rows' => $rows,
       '#attributes' => array('id' => "language-negotiation-methods-$type"),
     );
+    $options = array(
+      'table_id' => "language-negotiation-methods-$type",
+      'action' => 'order',
+      'relationship' => 'sibling',
+      'group' => "language-method-weight-$type",
+    );
+    drupal_attach_tabledrag($build, $options);
+
     $table  = drupal_render($form[$type]['configurable']);
     $table .= drupal_render($build);
     $table .= drupal_render_children($form[$type]);
 
-    drupal_add_tabledrag("language-negotiation-methods-$type", 'order', 'sibling', "language-method-weight-$type");
-
     $output .= '<div class="form-item table-language-group table-' . $type . '-wrapper">' . $title . $description . $table . '</div>';
   }
 
diff --git a/core/modules/menu/menu.admin.inc b/core/modules/menu/menu.admin.inc
index 2fe9455..d34f71e 100644
--- a/core/modules/menu/menu.admin.inc
+++ b/core/modules/menu/menu.admin.inc
@@ -17,9 +17,6 @@
 function theme_menu_overview_form($variables) {
   $form = $variables['form'];
 
-  drupal_add_tabledrag('menu-overview', 'match', 'parent', 'menu-plid', 'menu-plid', 'menu-mlid', TRUE, MENU_MAX_DEPTH - 1);
-  drupal_add_tabledrag('menu-overview', 'order', 'sibling', 'menu-weight');
-
   $header = array(
     t('Menu link'),
     array('data' => t('Enabled'), 'class' => array('checkbox')),
@@ -69,6 +66,24 @@ function theme_menu_overview_form($variables) {
       'id' => 'menu-overview',
     ),
   );
+  $match_options = array(
+    'table_id' => 'book-outline',
+    'action' => 'match',
+    'relationship' => 'parent',
+    'group' => 'menu-plid',
+    'subgroup' => 'menu-plid',
+    'source' => 'menu-mlid',
+    'hidden' => TRUE,
+    'limit' => MENU_MAX_DEPTH - 1,
+  );
+  $order_options = array(
+    'table_id' => 'book-outline',
+    'action' => 'order',
+    'relationship' => 'sibling',
+    'group' => 'menu-weight',
+  );
+  drupal_attach_tabledrag($table, $match_options);
+  drupal_attach_tabledrag($table, $order_options);
 
   $output .= drupal_render($form['inline_actions']);
   $output .= drupal_render($table);
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
index 29da4a8..019b3a5 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
@@ -31,7 +31,11 @@ public function form(array $form, array &$form_state) {
       '#empty' => t('No shortcuts available. @link', array('@link' => l(t('Add a shortcut'), 'admin/config/user-interface/shortcut/' . $this->entity->id() . '/add-link'))),
       '#attributes' => array('id' => 'shortcuts'),
       '#tabledrag' => array(
-        array('order', 'sibling', 'shortcut-weight'),
+        array(
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => 'shortcut-weight',
+        ),
       ),
     );
 
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
index 25edaad..9bd534d 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
@@ -307,20 +307,18 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
 
     if ($parent_fields) {
       $form['terms']['#tabledrag'][] = array(
-        'match',
-        'parent',
-        'term-parent',
-        'term-parent',
-        'term-id',
-        FALSE,
+        'action' => 'match',
+        'relationship' => 'parent',
+        'group' => 'term-parent',
+        'subgroup' => 'term-parent',
+        'source' => 'term-id',
+        'hidden' => FALSE,
       );
       $form['terms']['#tabledrag'][] = array(
-        'depth',
-        'group',
-        'term-depth',
-        NULL,
-        NULL,
-        FALSE
+        'action' => 'depth',
+        'relationship' => 'group',
+        'group' => 'term-depth',
+        'hidden' => FALSE,
       );
       $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
       $form['terms']['#attached']['js'][] = array(
@@ -328,7 +326,11 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
         'type' => 'setting',
       );
     }
-    $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
+    $form['terms']['#tabledrag'][] = array(
+      'action' => 'order',
+      'relationship' => 'sibling',
+      'group' => 'term-weight',
+    );
 
     if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
       $form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php
index dff2c6f..300d65e 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/Rearrange.php
@@ -77,7 +77,9 @@ public function buildForm(array $form, array &$form_state) {
       '#header' => array('', $this->t('Weight'), $this->t('Remove')),
       '#empty' => $this->t('No fields available.'),
       '#tabledrag' => array(
-        array('order', 'sibling', 'weight'),
+        'action' => 'order',
+        'relationship' => 'sibling',
+        'group' => 'weight',
       ),
       '#tree' => TRUE,
       '#prefix' => '<div class="scroll" data-drupal-views-scroll>',
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php
index 1bdf851..8d4c841 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ReorderDisplays.php
@@ -60,7 +60,9 @@ public function buildForm(array $form, array &$form_state) {
       '#header' => array($this->t('Display'), $this->t('Weight'), $this->t('Remove')),
       '#empty' => $this->t('No displays available.'),
       '#tabledrag' => array(
-        array('order', 'sibling', 'weight'),
+        'action' => 'order',
+        'relationship' => 'sibling',
+        'group' => 'weight',
       ),
       '#tree' => TRUE,
       '#prefix' => '<div class="scroll" data-drupal-views-scroll>',
diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc
index a13467e..a3f7882 100644
--- a/core/modules/views_ui/views_ui.theme.inc
+++ b/core/modules/views_ui/views_ui.theme.inc
@@ -207,7 +207,14 @@ function theme_views_ui_build_group_filter_form($variables) {
       'id' => 'views-filter-groups',
     ),
   );
-  drupal_add_tabledrag('views-filter-groups', 'order', 'sibling', 'weight');
+  $options = array(
+    'table_id' => 'views-filter-groups',
+    'action' => 'order',
+    'relationship' => 'sibling',
+    'group' => 'weight',
+  );
+
+  drupal_attach_tabledrag($table, $options);
   $render_form = drupal_render_children($form);
   return $output . $render_form . drupal_render($table) . drupal_render($form['add_group']) . $more;
 }
@@ -226,7 +233,14 @@ function theme_views_ui_rearrange_filter_form(&$variables) {
     if ($group_id !== 'ungroupable') {
       // Set up tabledrag so that it changes the group dropdown when rows are
       // dragged between groups.
-      drupal_add_tabledrag('views-rearrange-filters', 'match', 'sibling', 'views-group-select', 'views-group-select-' . $group_id);
+      $options = array(
+        'table_id' => 'views-rearrange-filters',
+        'action' => 'match',
+        'relationship' => 'sibling',
+        'group' => 'views-group-select',
+        'subgroup' => 'views-group-select-' . $group_id,
+      );
+      drupal_attach_tabledrag($form['override'], $options);
 
       // Title row, spanning all columns.
       $row = array();
@@ -286,7 +300,6 @@ function theme_views_ui_rearrange_filter_form(&$variables) {
   }
 
   if (!empty($ungroupable_rows)) {
-    drupal_add_tabledrag('views-rearrange-filters-ungroupable', 'order', 'sibling', 'weight');
     $header = array(t('Ungroupable filters'), t('Weight'), array('class' => array('views-hide-label'), 'data' => t('Group')), array('class' => array('views-hide-label'), 'data' => t('Remove')));
     $table = array(
       '#theme' => 'table',
@@ -297,11 +310,17 @@ function theme_views_ui_rearrange_filter_form(&$variables) {
         'class' => array('arrange'),
       ),
     );
+    $options = array(
+      'table_id' => 'views-rearrange-filters-ungroupable',
+      'action' => 'order',
+      'relationship' =>  'sibling',
+      'group' =>  'weight',
+    );
+    drupal_attach_tabledrag($table, $options);
     $output .= drupal_render($table);
   }
 
   // Set up tabledrag so that the weights are changed when rows are dragged.
-  drupal_add_tabledrag('views-rearrange-filters', 'order', 'sibling', 'weight');
   $table = array(
     '#theme' => 'table',
     '#rows' => $rows,
@@ -310,6 +329,8 @@ function theme_views_ui_rearrange_filter_form(&$variables) {
       'class' => array('arrange'),
     ),
   );
+  $options['table_id'] = 'views-rearrange-filters';
+  drupal_attach_tabledrag($table, $options);
   $output .= drupal_render($table);
   $output .= '</div>';
 
