diff --git a/includes/form.inc b/includes/form.inc
index 3b2032eb72..6dbf7f6d02 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -3481,7 +3481,21 @@ function theme_tableselect($variables) {
       // As theme_table only maps header and row columns by order, create the
       // correct order by iterating over the header fields.
       foreach ($element['#header'] as $fieldname => $title) {
-        $row['data'][] = $element['#options'][$key][$fieldname];
+        // A row cell can span over multiple headers, which means less row cells
+        // than headers could be present.
+        if (isset($element['#options'][$key][$fieldname])) {
+          // A header can span over multiple cells and in this case the cells
+          // are passed in an array. The order of this array determines the
+          // order in which they are added.
+          if (!isset($element['#options'][$key][$fieldname]['data']) && is_array($element['#options'][$key][$fieldname])) {
+            foreach ($element['#options'][$key][$fieldname] as $cell) {
+              $row['data'][] = $cell;
+            }
+          }
+          else {
+            $row['data'][] = $element['#options'][$key][$fieldname];
+          }
+        }
       }
       $rows[] = $row;
     }
diff --git a/misc/tabledrag.js b/misc/tabledrag.js
index 7ea88b607a..1d935f1f55 100644
--- a/misc/tabledrag.js
+++ b/misc/tabledrag.js
@@ -187,13 +187,21 @@ Drupal.tableDrag.prototype.initColumns = function () {
  * Undo showColumns().
  */
 Drupal.tableDrag.prototype.hideColumns = function () {
+  // Count the table header tags that are meant to be hidden.
+  var th_hidden = $('th.tabledrag-hide', 'table.tabledrag-processed').length;
+  // Get the sum of each th's colspan attributes.
+  var th_colspan = $('> thead > tr > th', 'table.tabledrag-processed').map(function() {
+    return parseInt($(this).prop('colSpan'));
+  }).get().reduce(function(acc, current) {
+    return acc + current;
+  }, 0);
   // Hide weight/parent cells and headers.
   $('.tabledrag-hide', 'table.tabledrag-processed').css('display', 'none');
   // Show TableDrag handles.
   $('.tabledrag-handle', 'table.tabledrag-processed').css('display', '');
   // Reduce the colspan of any effected multi-span columns.
   $('.tabledrag-has-colspan', 'table.tabledrag-processed').each(function () {
-    this.colSpan = this.colSpan - 1;
+    this.colSpan = th_colspan - th_hidden;
   });
   // Change link text.
   $('.tabledrag-toggle-weight').text(Drupal.t('Show row weights'));
@@ -212,13 +220,19 @@ Drupal.tableDrag.prototype.hideColumns = function () {
  * Undo hideColumns().
  */
 Drupal.tableDrag.prototype.showColumns = function () {
+  // Get the sum of each th's colspan attributes.
+  var th_colspan = $('> thead > tr > th', 'table.tabledrag-processed').map(function() {
+    return parseInt($(this).prop('colSpan'));
+  }).get().reduce(function(acc, current) {
+    return acc + current;
+  }, 0);
   // Show weight/parent cells and headers.
   $('.tabledrag-hide', 'table.tabledrag-processed').css('display', '');
   // Hide TableDrag handles.
   $('.tabledrag-handle', 'table.tabledrag-processed').css('display', 'none');
   // Increase the colspan for any columns where it was previously reduced.
   $('.tabledrag-has-colspan', 'table.tabledrag-processed').each(function () {
-    this.colSpan = this.colSpan + 1;
+    this.colSpan = th_colspan;
   });
   // Change link text.
   $('.tabledrag-toggle-weight').text(Drupal.t('Hide row weights'));
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index e52c8c42e1..40b5f357b4 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -892,6 +892,31 @@ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase {
     }
   }
 
+   /**
+   * Tests the display when #colspan is set.
+   */
+  function testTableselectColSpan() {
+    $this->drupalGet('form_test/tableselect/colspan');
+
+    $this->assertText(t('Three'), 'Presence of the third column');
+    $this->assertNoText(t('Four'), 'Absence of a fourth column');
+
+    // There should be three labeled column headers and 1 for the input.
+    $table_head = $this->xpath('//thead');
+    $this->assertEqual(count($table_head[0]->tr->th), 4, 'There are four column headers');
+
+    $table_body = $this->xpath('//tbody');
+    // The first two body rows should each have 5 table cells: One for the
+    // radio, one cell in the first column, one cell in the the second column,
+    // and two cells in the third column which has colspan 2.
+    for ( $i = 0; $i <= 1; $i++) {
+      $this->assertEqual(count($table_body[0]->tr[$i]->td), 5, format_string('There are five cells in row @row.', array('@row' => $i)));
+    }
+    // The third row should have 3 cells, one for the radio, one spanning the
+    // first and second column, and a third in column 3 (which has colspan 3).
+    $this->assertEqual(count($table_body[0]->tr[2]->td), 3, 'There are three cells in row 3.');
+  }
+
   /**
    * Test the display of the #empty text when #options is an empty array.
    */
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 9f071826e5..b63763de30 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -66,6 +66,13 @@ function form_test_menu() {
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
   );
+  $items['form_test/tableselect/colspan'] = array(
+    'title' => 'Tableselect colspan test',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('_form_test_tableselect_colspan_form'),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
   $items['form_test/tableselect/empty-text'] = array(
     'title' => 'Tableselect empty text test',
     'page callback' => 'drupal_get_form',
@@ -662,6 +669,29 @@ function _form_test_tableselect_multiple_false_form($form, $form_state) {
   return _form_test_tableselect_form_builder($form, $form_state, array('#multiple' => FALSE));
 }
 
+ /**
+ * Test the tableselect #colspan functionality.
+ */
+function _form_test_tableselect_colspan_form($form, $form_state) {
+  list($header, $options) = _form_test_tableselect_get_data();
+
+  // Change the data so that the third column has colspan=2.
+  $header['three'] = array('data' => 'Three', 'colspan' => 2);
+  unset($header['four']);
+  // Set the each row so that column 3 is an array.
+  foreach ($options as $name => $row) {
+    $options[$name]['three'] = array($row['three'], $row['four']);
+    unset($options[$name]['four']);
+  }
+  // Combine cells in row 3.
+  $options['row3']['one'] = array('data' => $options['row3']['one'], 'colspan' => 2);
+  unset($options['row3']['two']);
+  $options['row3']['three'] = array('data' => $options['row3']['three'][0], 'colspan' => 2);
+  unset($options['row3']['four']);
+
+  return _form_test_tableselect_form_builder($form, $form_state, array('#header' => $header, '#options' => $options));
+}
+
 /**
  * Process the tableselect #multiple = FALSE submitted values.
  */
