diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 01909a756e3616cd2e81fd46162f22ec55a218a1..06579085cf97e3395f41b266d30c909af05bc2e0 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -678,6 +678,14 @@ drupal.tabledrag:
     - core/once
     - core/drupal.touchevents-test
 
+drupal.tabledrag.ajax:
+  version: VERSION
+  js:
+    misc/tabledrag-ajax.js: { }
+  dependencies:
+    - core/ajax
+    - core/tabledrag
+
 drupal.tableheader:
   version: VERSION
   js:
diff --git a/core/lib/Drupal/Core/Ajax/TabledragWarningCommand.php b/core/lib/Drupal/Core/Ajax/TabledragWarningCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..e1cc60892e32a37e271282d8cc11a9d797b2b766
--- /dev/null
+++ b/core/lib/Drupal/Core/Ajax/TabledragWarningCommand.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\Core\Ajax;
+
+use Drupal\Core\Asset\AttachedAssets;
+
+/**
+ * AJAX command for conveying changed tabledrag rows.
+ *
+ * This command is provided an id of a table row then does the following:
+ * - Marks the row as changed.
+ * - If a message generated by the tableDragChangedWarning is not present above
+ *   the table the row belongs to, that message is added there.
+ *
+ * @see Drupal.AjaxCommands.prototype.tabledragChanged
+ *
+ * @ingroup ajax
+ */
+class TabledragWarningCommand implements CommandInterface, CommandWithAttachedAssetsInterface {
+
+  /**
+   * Constructs a TableDragWarningCommand object.
+   *
+   * @param string $id
+   *   The id of the changed row.
+   * @param string $tabledrag_instance
+   *   The identifier of the tabledrag instance.
+   */
+  public function __construct(
+    protected string $id,
+    protected string $tabledrag_instance) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    return [
+      'command' => 'tabledragChanged',
+      'id' => $this->id,
+      'tabledrag_instance' => $this->tabledrag_instance,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAttachedAssets() {
+    $assets = new AttachedAssets();
+    $assets->setLibraries(['core/drupal.tabledrag.ajax']);
+    return $assets;
+  }
+
+}
diff --git a/core/misc/tabledrag-ajax.js b/core/misc/tabledrag-ajax.js
new file mode 100644
index 0000000000000000000000000000000000000000..11df818dd8855eae3b47067b86d40780709e6b38
--- /dev/null
+++ b/core/misc/tabledrag-ajax.js
@@ -0,0 +1,36 @@
+/**
+ * Ajax command for highlighting elements.
+ *
+ * @param {Drupal.Ajax} [ajax]
+ *   An Ajax object.
+ * @param {object} response
+ *   The Ajax response.
+ * @param {string} response.id
+ *   The row id.
+ * @param {string} response.tabledrag_instance
+ *   The tabledrag instance identifier.
+ * @param {number} [status]
+ *   The HTTP status code.
+ */
+Drupal.AjaxCommands.prototype.tabledragChanged = function (
+  ajax,
+  response,
+  status,
+) {
+  if (status !== 'success') {
+    return;
+  }
+
+  const tableDrag = Drupal.tableDrag[response.tabledrag_instance];
+
+  // eslint-disable-next-line new-cap
+  const rowObject = new tableDrag.row(
+    document.getElementById(response.id),
+    '',
+    tableDrag.indentEnabled,
+    tableDrag.maxDepth,
+    true,
+  );
+  rowObject.markChanged();
+  rowObject.addChangedWarning();
+};
diff --git a/core/misc/tabledrag.js b/core/misc/tabledrag.js
index ea05dc8215edf19177c1539a0b29f9ad4ad796cc..bd25008347fe507a07496da2f4d5eaecc3249b6f 100644
--- a/core/misc/tabledrag.js
+++ b/core/misc/tabledrag.js
@@ -180,6 +180,14 @@
      * @type {boolean}
      */
     this.indentEnabled = false;
+
+    /**
+     * Keeps track of rows that have changed.
+     */
+    this.changedRowIds = Drupal.tableDrag[table.id]
+      ? Drupal.tableDrag[table.id].changedRowIds
+      : new Set();
+
     Object.keys(tableSettings || {}).forEach((group) => {
       Object.keys(tableSettings[group] || {}).forEach((n) => {
         if (tableSettings[group][n].relationship === 'parent') {
@@ -269,6 +277,20 @@
         }
       }, this),
     );
+
+    // Check for any rows marked as changed before this tabledrag was rerendered
+    // and mark them as changed for this current render.
+    this.changedRowIds.forEach((changedRowId) => {
+      // eslint-disable-next-line new-cap
+      const rowObject = new self.row(
+        document.getElementById(changedRowId),
+        '',
+        self.indentEnabled,
+        self.maxDepth,
+        true,
+      );
+      rowObject.markChanged();
+    });
   };
 
   /**
@@ -842,10 +864,7 @@
 
         self.rowObject.markChanged();
         if (self.changed === false) {
-          $(Drupal.theme('tableDragChangedWarning'))
-            .insertBefore(self.table)
-            .hide()
-            .fadeIn('slow');
+          self.rowObject.addChangedWarning();
           self.changed = true;
         }
       }
@@ -1334,6 +1353,28 @@
     }
   };
 
+  /**
+   * Adds a warning above the table informing users they must save changes.
+   */
+  Drupal.tableDrag.prototype.row.prototype.addChangedWarning = function () {
+    // Do not add the changed warning if one is already present.
+    if (!$(this.table.parentNode).find('.tabledrag-changed-warning').length) {
+      const $form = $(this.table).closest('form');
+      $(Drupal.theme('tableDragChangedWarning'))
+        .insertBefore(this.table)
+        .hide()
+        // If a warning has already been shown, do not fade the warning in, so
+        // it appears static when the table is rebuilt.
+        .fadeIn(
+          $form[0].hasAttribute('data-tabledrag-save-warning') ? 0 : 'slow',
+        );
+
+      // Keep track of the warning having been added in an element that lives
+      // outside the table which rebuilds when certain changes occur.
+      $form[0].setAttribute('data-tabledrag-save-warning', true);
+    }
+  };
+
   /**
    * Find all children of rowObject by indentation.
    *
@@ -1619,6 +1660,7 @@
     if (cell.find('abbr.tabledrag-changed').length === 0) {
       cell.append(marker);
     }
+    Drupal.tableDrag[this.table.id].changedRowIds.add(this.element.id);
   };
 
   /**
diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php
index a20df5ee56b1c7cb5610acc4fd46b38702c91970..60ccc9e85fc272f628568eb57b3aa7b1469cad12 100644
--- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php
+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5AllowedTagsTest.php
@@ -382,6 +382,7 @@ public function testMediaElementAllowedTags() {
     $this->assertNotNull($assert_session->waitForElementVisible('css', '[data-drupal-selector=edit-filters-media-embed-settings]', 0));
 
     $page->clickLink('Embed media');
+    $assert_session->waitForField('filters[media_embed][settings][allowed_view_modes][view_mode_2]');
     $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_1]');
     $page->checkField('filters[media_embed][settings][allowed_view_modes][view_mode_2]');
     $assert_session->assertWaitOnAjaxRequest();
diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js
index d6e2b5c55e59c221e13e63af9eeefeacc138d2ed..a381226dfb1013d7dae1590df04c83ff4b7fe058 100644
--- a/core/modules/field_ui/field_ui.js
+++ b/core/modules/field_ui/field_ui.js
@@ -149,8 +149,14 @@
     onChange() {
       const $trigger = $(this);
       const $row = $trigger.closest('tr');
-      const rowHandler = $row.data('fieldUIRowHandler');
 
+      // Do not fire change listeners for items within forms that have their
+      // own AJAX callbacks to process a change.
+      if ($trigger.closest('.ajax-new-content').length !== 0) {
+        return;
+      }
+
+      const rowHandler = $row.data('fieldUIRowHandler');
       const refreshRows = {};
       refreshRows[rowHandler.name] = $trigger.get(0);
 
@@ -168,8 +174,27 @@
         rowHandler.region = region;
       }
 
-      // Ajax-update the rows.
-      Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
+      // Fields inside `.tabledrag-hide` are typically hidden. They can be
+      // visible when "Show row weights" are enabled. If their value is changed
+      // while visible, the row should be marked as changed, but they should not
+      // be processed via AJAXRefreshRows as they are intended to be fields AJAX
+      // updates the value of.
+      if ($trigger.closest('.tabledrag-hide').length) {
+        const thisTableDrag = Drupal.tableDrag['field-display-overview'];
+        // eslint-disable-next-line new-cap
+        const rowObject = new thisTableDrag.row(
+          $row[0],
+          '',
+          thisTableDrag.indentEnabled,
+          thisTableDrag.maxDepth,
+          true,
+        );
+        rowObject.markChanged();
+        rowObject.addChangedWarning();
+      } else {
+        // Ajax-update the rows.
+        Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
+      }
     },
 
     /**
@@ -262,7 +287,6 @@
         rowNames.push(rowName);
         ajaxElements.push(rows[rowName]);
       });
-
       if (rowNames.length) {
         // Add a throbber next each of the ajaxElements.
         $(ajaxElements).after(Drupal.theme.ajaxProgressThrobber());
@@ -285,9 +309,11 @@
           // jQuery trigger().
           $(input).on('mousedown', () => {
             returnFocus = {
-              drupalSelector: document.activeElement.getAttribute(
+              drupalSelector: document.activeElement.hasAttribute(
                 'data-drupal-selector',
-              ),
+              )
+                ? document.activeElement.getAttribute('data-drupal-selector')
+                : false,
               scrollY: window.scrollY,
             };
           });
@@ -300,14 +326,13 @@
                   `[data-drupal-selector="${returnFocus.drupalSelector}"]`,
                 )
                 .focus();
-
-              // Ensure the scroll position is the same as when the input was
-              // initially changed.
-              window.scrollTo({
-                top: returnFocus.scrollY,
-              });
-              returnFocus = {};
             }
+            // Ensure the scroll position is the same as when the input was
+            // initially changed.
+            window.scrollTo({
+              top: returnFocus.scrollY,
+            });
+            returnFocus = {};
           });
         });
         $('input[data-drupal-selector="edit-refresh"]').trigger('mousedown');
@@ -347,14 +372,11 @@
     this.region = data.region;
     this.tableDrag = data.tableDrag;
     this.defaultPlugin = data.defaultPlugin;
-
-    // Attach change listener to the 'plugin type' select.
     this.$pluginSelect = $(row).find('.field-plugin-type');
-    this.$pluginSelect.on('change', Drupal.fieldUIOverview.onChange);
-
-    // Attach change listener to the 'region' select.
     this.$regionSelect = $(row).find('select.field-region');
-    this.$regionSelect.on('change', Drupal.fieldUIOverview.onChange);
+
+    // Attach change listeners to select and input elements in the row.
+    $(row).find('select, input').on('change', Drupal.fieldUIOverview.onChange);
 
     return this;
   };
diff --git a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
index 6cc3f6bd058977fef35930c0c5b1977706d103b5..a92618a728b5aeb28782a7bafc6d6194eea63531 100644
--- a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
+++ b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
@@ -4,6 +4,10 @@
 
 use Drupal\Component\Plugin\Factory\DefaultFactory;
 use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Ajax\TabledragWarningCommand;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Entity\EntityInterface;
@@ -299,7 +303,7 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, arr
 
     // Disable fields without any applicable plugins.
     if (empty($this->getApplicablePluginOptions($field_definition))) {
-      $this->entity->removeComponent($field_name)->save();
+      $this->entity->removeComponent($field_name);
       $display_options = $this->entity->getComponent($field_name);
     }
 
@@ -679,6 +683,7 @@ public function multistepSubmit($form, FormStateInterface $form_state) {
    * Ajax handler for multistep buttons.
    */
   public function multistepAjax($form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
     $trigger = $form_state->getTriggeringElement();
     $op = $trigger['#op'];
 
@@ -709,8 +714,19 @@ public function multistepAjax($form, FormStateInterface $form_state) {
       }
     }
 
-    // Return the whole table.
-    return $form['fields'];
+    // Replace the whole table.
+    $response->addCommand(new ReplaceCommand('#field-display-overview-wrapper', $form['fields']));
+
+    // Add "row updated" warning after the table has been replaced.
+    if (!in_array($op, ['cancel', 'edit'])) {
+      foreach ($updated_rows as $name) {
+        // The ID of the rendered table row is `$name` processed by getClass().
+        // @see \Drupal\field_ui\Element\FieldUiTable::tablePreRender
+        $response->addCommand(new TabledragWarningCommand(Html::getClass($name), 'field-display-overview'));
+      }
+    }
+
+    return $response;
   }
 
   /**
diff --git a/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php b/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a2fb83175b324aff13f2f419918c528057ea9410
--- /dev/null
+++ b/core/modules/field_ui/tests/src/Functional/EntityDisplayFormBaseTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\Tests\field_ui\Functional;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the UI for configuring entity displays.
+ *
+ * @group field_ui
+ */
+class EntityDisplayFormBaseTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['field_ui', 'entity_test', 'field_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    foreach (entity_test_entity_types() as $entity_type) {
+      // Auto-create fields for testing.
+      FieldStorageConfig::create([
+        'entity_type' => $entity_type,
+        'field_name' => 'field_test_no_plugin',
+        'type' => 'field_test',
+        'cardinality' => 1,
+      ])->save();
+      FieldConfig::create([
+        'entity_type' => $entity_type,
+        'field_name' => 'field_test_no_plugin',
+        'bundle' => $entity_type,
+        'label' => 'Test field with no plugin',
+        'translatable' => FALSE,
+      ])->save();
+
+      \Drupal::service('entity_display.repository')
+        ->getFormDisplay($entity_type, $entity_type)
+        ->setComponent('field_test_no_plugin', [])
+        ->save();
+    }
+
+    $this->drupalLogin($this->drupalCreateUser([
+      'administer entity_test form display',
+    ]));
+  }
+
+  /**
+   * Ensures the entity is not affected when there are no applicable formatters.
+   */
+  public function testNoApplicableFormatters(): void {
+    $storage = $this->container->get('entity_type.manager')->getStorage('entity_form_display');
+    $id = 'entity_test.entity_test.default';
+
+    $entity_before = $storage->load($id);
+    $this->drupalGet('entity_test/structure/entity_test/form-display');
+    $entity_after = $storage->load($id);
+
+    $this->assertSame($entity_before->toArray(), $entity_after->toArray());
+  }
+
+}
diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageDisplayTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageDisplayTest.php
index 03ed77eb7a28c37597e1b94540c914ee2db2a23a..fbc618549485388e44c1fb82abef169667bb1f45 100644
--- a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageDisplayTest.php
+++ b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageDisplayTest.php
@@ -485,4 +485,85 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi
     $this->assertNotEmpty($row, 'Field was created and appears in the overview page.');
   }
 
+  /**
+   * Confirms that notifications to save appear when necessary.
+   */
+  public function testNotAppliedUntilSavedWarning() {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    // Admin Manage Fields page.
+    $manage_fields = 'admin/structure/types/manage/' . $this->type;
+
+    $this->fieldUIAddNewField($manage_fields, 'test', 'Test field');
+    $manage_display = 'admin/structure/types/manage/' . $this->type . '/display';
+    $manage_form = 'admin/structure/types/manage/' . $this->type . '/form-display';
+
+    // Form display, change widget type.
+    $this->drupalGet($manage_form);
+    $assert_session->elementNotExists('css', '.tabledrag-changed-warning');
+    $assert_session->elementNotExists('css', 'abbr.tabledrag-changed');
+    $page->selectFieldOption('fields[uid][type]', 'options_buttons');
+    $this->assertNotNull($changed_warning = $assert_session->waitForElementVisible('css', '.tabledrag-changed-warning'));
+    $this->assertNotNull($assert_session->waitForElementVisible('css', ' #uid abbr.tabledrag-changed'));
+    $this->assertSame('* You have unsaved changes.', $changed_warning->getText());
+
+    // Form display, change widget settings.
+    $this->drupalGet($manage_form);
+    $edit_widget_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-uid-settings-edit"]');
+    $edit_widget_button->press();
+    $assert_session->waitForText('3rd party formatter settings form');
+
+    // Confirm the AJAX operation of opening the form does not result in the row
+    // being set as changed. New settings must be submitted for that to happen.
+    $assert_session->elementNotExists('css', 'abbr.tabledrag-changed');
+    $cancel_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-uid-settings-edit-form-actions-cancel-settings"]');
+    $cancel_button->press();
+    $assert_session->assertNoElementAfterWait('css', '[data-drupal-selector="edit-fields-uid-settings-edit-form-actions-cancel-settings"]');
+    $assert_session->elementNotExists('css', '.tabledrag-changed-warning');
+    $assert_session->elementNotExists('css', 'abbr.tabledrag-changed');
+    $edit_widget_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-uid-settings-edit"]');
+    $edit_widget_button->press();
+    $widget_field = $assert_session->waitForField('fields[uid][settings_edit_form][third_party_settings][field_third_party_test][field_test_widget_third_party_settings_form]');
+    $widget_field->setValue('honk');
+    $update_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-uid-settings-edit-form-actions-save-settings"]');
+    $update_button->press();
+    $assert_session->assertNoElementAfterWait('css', '[data-drupal-selector="edit-fields-field-test-settings-edit-form-actions-cancel-settings"]');
+    $this->assertNotNull($changed_warning = $assert_session->waitForElementVisible('css', '.tabledrag-changed-warning'));
+    $this->assertNotNull($assert_session->waitForElementVisible('css', ' #uid abbr.tabledrag-changed'));
+    $this->assertSame('* You have unsaved changes.', $changed_warning->getText());
+
+    // Content display, change formatter type.
+    $this->drupalGet($manage_display);
+    $assert_session->elementNotExists('css', '.tabledrag-changed-warning');
+    $assert_session->elementNotExists('css', 'abbr.tabledrag-changed');
+    $page->selectFieldOption('edit-fields-field-test-label', 'inline');
+    $this->assertNotNull($changed_warning = $assert_session->waitForElementVisible('css', '.tabledrag-changed-warning'));
+    $this->assertNotNull($assert_session->waitForElementVisible('css', ' #field-test abbr.tabledrag-changed'));
+    $this->assertSame('* You have unsaved changes.', $changed_warning->getText());
+
+    // Content display, change formatter settings.
+    $this->drupalGet($manage_display);
+    $assert_session->elementNotExists('css', '.tabledrag-changed-warning');
+    $assert_session->elementNotExists('css', 'abbr.tabledrag-changed');
+    $edit_formatter_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-field-test-settings-edit"]');
+    $edit_formatter_button->press();
+    $assert_session->waitForText('3rd party formatter settings form');
+    $cancel_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-field-test-settings-edit-form-actions-cancel-settings"]');
+    $cancel_button->press();
+    $assert_session->assertNoElementAfterWait('css', '[data-drupal-selector="edit-fields-field-test-settings-edit-form-actions-cancel-settings"]');
+    $assert_session->elementNotExists('css', '.tabledrag-changed-warning');
+    $assert_session->elementNotExists('css', 'abbr.tabledrag-changed');
+    $edit_formatter_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-field-test-settings-edit"]');
+    $edit_formatter_button->press();
+    $formatter_field = $assert_session->waitForField('fields[field_test][settings_edit_form][third_party_settings][field_third_party_test][field_test_field_formatter_third_party_settings_form]');
+    $formatter_field->setValue('honk');
+    $update_button = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-fields-field-test-settings-edit-form-actions-save-settings"]');
+    $update_button->press();
+    $assert_session->assertNoElementAfterWait('css', '[data-drupal-selector="edit-fields-field-test-settings-edit-form-actions-cancel-settings"]');
+    $this->assertNotNull($changed_warning = $assert_session->waitForElementVisible('css', '.tabledrag-changed-warning'));
+    $this->assertNotNull($assert_session->waitForElementVisible('css', ' #field-test abbr.tabledrag-changed'));
+    $this->assertSame('* You have unsaved changes.', $changed_warning->getText());
+  }
+
 }
diff --git a/core/phpstan-baseline.neon b/core/phpstan-baseline.neon
index efb5e3976492c0b3c80a979655346c32de0533fe..a09976ba7d15bd274a1e9397be6c72b833993c08 100644
--- a/core/phpstan-baseline.neon
+++ b/core/phpstan-baseline.neon
@@ -1217,7 +1217,7 @@ parameters:
 
 		-
 			message: "#^Variable \\$updated_rows might not be defined\\.$#"
-			count: 1
+			count: 2
 			path: modules/field_ui/src/Form/EntityDisplayFormBase.php
 
 		-
diff --git a/core/themes/claro/js/tabledrag.js b/core/themes/claro/js/tabledrag.js
index b6a9d331116bec17c014ff3592ee4e1f8515f7de..79df748de153775e397be966b66f346fb4d10fed 100644
--- a/core/themes/claro/js/tabledrag.js
+++ b/core/themes/claro/js/tabledrag.js
@@ -122,6 +122,7 @@
       if (cell.find('.js-tabledrag-changed-marker').length === 0) {
         cell.find('.js-tabledrag-handle').after(marker);
       }
+      Drupal.tableDrag[this.table.id].changedRowIds.add(this.element.id);
     },
 
     /**