diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js
index 8f4c6c71c1..52e54f4263 100644
--- a/core/misc/ajax.es6.js
+++ b/core/misc/ajax.es6.js
@@ -1038,7 +1038,7 @@
   Drupal.Ajax.prototype.success = function (response, status) {
     // Remove the progress element.
     if (this.progress.element) {
-      $(this.progress.element).remove();
+        $('.' + $(this.progress.element).attr('class').replace(/\s/g, '.'),).remove();
     }
     if (this.progress.object) {
       this.progress.object.stopMonitoring();
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index 9abcadf5ae..de5c2198ab 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -379,7 +379,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
   Drupal.Ajax.prototype.success = function (response, status) {
     var _this3 = this;
     if (this.progress.element) {
-      $(this.progress.element).remove();
+      $('.' + $(this.progress.element).attr('class').replace(/\s/g, '.')).remove();
     }
     if (this.progress.object) {
       this.progress.object.stopMonitoring();
@@ -639,4 +639,4 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
       }
     }
   });
-})(jQuery, window, Drupal, drupalSettings, loadjs, window.tabbable);
\ No newline at end of file
+})(jQuery, window, Drupal, drupalSettings, loadjs, window.tabbable);
diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaOverviewTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaOverviewTest.php
index 88e6cecb18..d3d5a6a784 100644
--- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaOverviewTest.php
+++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaOverviewTest.php
@@ -105,7 +105,7 @@ public function testAdministrationPage() {
 
     // Test that selecting elements as a part of bulk operations works.
     $page->selectFieldOption('Media type', '- Any -');
-    $assert_session->elementExists('css', '#views-exposed-form-media-library-page')->submit();
+    $assert_session->elementExists('css', '.views-exposed-form-media-library-page')->submit();
     $this->waitForText('Dog');
 
     // This tests that anchor tags clicked inside the preview are suppressed.
diff --git a/core/modules/views/js/ajax_view.es6.js b/core/modules/views/js/ajax_view.es6.js
index 9423bb1d64..1c02f288de 100644
--- a/core/modules/views/js/ajax_view.es6.js
+++ b/core/modules/views/js/ajax_view.es6.js
@@ -100,10 +100,10 @@
 
     // Add the ajax to exposed forms.
     this.$exposed_form = $(
-      `form#views-exposed-form-${settings.view_name.replace(
+      `form[id^="views-exposed-form-${settings.view_name.replace(
         /_/g,
         '-',
-      )}-${settings.view_display_id.replace(/_/g, '-')}`,
+      )}-${settings.view_display_id.replace(/_/g, '-')}"]`,
     );
     once('exposed-form', this.$exposed_form).forEach(
       $.proxy(this.attachExposedFormAjax, this),
diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js
index d824250714..b0d94a4251 100644
--- a/core/modules/views/js/ajax_view.js
+++ b/core/modules/views/js/ajax_view.js
@@ -55,7 +55,7 @@
       }
     };
     this.settings = settings;
-    this.$exposed_form = $("form#views-exposed-form-".concat(settings.view_name.replace(/_/g, '-'), "-").concat(settings.view_display_id.replace(/_/g, '-')));
+    this.$exposed_form = $("form[id^=\"views-exposed-form-".concat(settings.view_name.replace(/_/g, '-'), "-").concat(settings.view_display_id.replace(/_/g, '-'), "\"]"));
     once('exposed-form', this.$exposed_form).forEach($.proxy(this.attachExposedFormAjax, this));
     once('ajax-pager', this.$view.filter($.proxy(this.filterNestedViews, this))).forEach($.proxy(this.attachPagerAjax, this));
     var selfSettings = $.extend({}, this.element_settings, {
@@ -106,4 +106,4 @@
       }, 500);
     }
   };
-})(jQuery, Drupal, drupalSettings);
\ No newline at end of file
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/views/src/Controller/ViewAjaxController.php b/core/modules/views/src/Controller/ViewAjaxController.php
index bff0acb81b..75156a2c30 100644
--- a/core/modules/views/src/Controller/ViewAjaxController.php
+++ b/core/modules/views/src/Controller/ViewAjaxController.php
@@ -143,6 +143,7 @@ public function ajaxView(Request $request) {
         'view_dom_id',
         'pager_element',
         'view_base_path',
+        'view_html_id',
         AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
         FormBuilderInterface::AJAX_FORM_REQUEST,
         MainContentViewSubscriber::WRAPPER_FORMAT,
diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php
index bbeb4db276..9b0383b583 100644
--- a/core/modules/views/src/Form/ViewsExposedForm.php
+++ b/core/modules/views/src/Form/ViewsExposedForm.php
@@ -144,7 +144,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     $form['#action'] = $form_action;
     $form['#theme'] = $view->buildThemeFunctions('views_exposed_form');
-    $form['#id'] = Html::cleanCssIdentifier('views_exposed_form-' . $view->storage->id() . '-' . $display['id']);
+    $clean_form_id = Html::cleanCssIdentifier('views_exposed_form-' . $view->storage->id() . '-' . $display['id']);
+    $form['#attributes']['class'][] = $clean_form_id;
+    // The form can be possibly rendered twice on a page, make the id unique.
+    $form['#id'] = Html::getUniqueId($clean_form_id);
 
     /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form_plugin */
     $exposed_form_plugin = $view->display_handler->getPlugin('exposed_form');
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
index b653c35279..e26f054d09 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\views\Functional\Plugin;
 
+use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Utility\Html;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
@@ -58,7 +59,7 @@ protected function setUp($import_test_views = TRUE, $modules = ['views_test_conf
 
     $this->nodes = [];
     // Create some random nodes.
-    for ($i = 0; $i < 5; $i++) {
+    for ($i = 0; $i < 5; $i) {
       $this->nodes[] = $this->drupalCreateNode(['type' => 'article']);
       $this->nodes[] = $this->drupalCreateNode(['type' => 'page']);
     }
@@ -296,6 +297,21 @@ public function providerTestExposedBlock() {
     ];
   }
 
+  /**
+   * Test placing the same form twice on the same page.
+   */
+  public function testNoDoubleIdsForSameExposedForm() {
+    $this->drupalCreateContentType(['type' => 'page']);
+    $view = Views::getView('test_exposed_block');
+    $view->setDisplay('page_1');
+    $this->drupalPlaceBlock('views_exposed_filter_block:test_exposed_block-page_1');
+    $this->drupalPlaceBlock('views_exposed_filter_block:test_exposed_block-page_1');
+
+    $this->drupalGet('test_exposed_block');
+
+    $this->assertNoDuplicateIds();
+  }
+
   /**
    * Tests the input required exposed form type.
    */
@@ -348,7 +364,7 @@ public function testTextInputRequired() {
    * Tests exposed forms with exposed sort and items per page.
    */
   public function testExposedSortAndItemsPerPage() {
-    for ($i = 0; $i < 50; $i++) {
+    for ($i = 0; $i < 50; $i) {
       $entity = EntityTest::create([]);
       $entity->save();
     }
@@ -469,26 +485,26 @@ public function testFormErrorWithExposedForm() {
   public function testExposedFilterPagination() {
     $this->drupalCreateContentType(['type' => 'post']);
     // Create some random nodes.
-    for ($i = 0; $i < 5; $i++) {
+    for ($i = 0; $i < 5; $i) {
       $this->drupalCreateNode(['type' => 'post']);
     }
 
     $this->drupalGet('test_exposed_form_pager');
     $this->getSession()->getPage()->fillField('type[]', 'post');
     $this->getSession()->getPage()->fillField('created[min]', '-1 month');
-    $this->getSession()->getPage()->fillField('created[max]', '+1 month');
+    $this->getSession()->getPage()->fillField('created[max]', '1 month');
 
     // Ensure the filters can be applied.
     $this->getSession()->getPage()->pressButton('Apply');
     $this->assertTrue($this->assertSession()->optionExists('type[]', 'post')->isSelected());
     $this->assertSession()->fieldValueEquals('created[min]', '-1 month');
-    $this->assertSession()->fieldValueEquals('created[max]', '+1 month');
+    $this->assertSession()->fieldValueEquals('created[max]', '1 month');
 
     // Ensure the filters are still applied after pressing next.
     $this->clickLink('Next ›');
     $this->assertTrue($this->assertSession()->optionExists('type[]', 'post')->isSelected());
     $this->assertSession()->fieldValueEquals('created[min]', '-1 month');
-    $this->assertSession()->fieldValueEquals('created[max]', '+1 month');
+    $this->assertSession()->fieldValueEquals('created[max]', '1 month');
   }
 
   /**
@@ -510,6 +526,31 @@ protected function assertNodesExist(array $bundles): void {
     }
   }
 
+  /**
+   * Asserts that each HTML ID is used for just a single element on the page.
+   */
+  protected function assertNoDuplicateIds() {
+    $args = ['@url' => $this->getUrl()];
+
+    if (!$elements = $this->xpath('//*[@id]')) {
+      $this->fail(new FormattableMarkup('The page @url contains no HTML IDs.', $args));
+      return;
+    }
+
+    $message = new FormattableMarkup('The page @url contains duplicate HTML IDs', $args);
+
+    $seen_ids = [];
+    foreach ($elements as $element) {
+      $id = $element->getAttribute('id');
+      if (isset($seen_ids[$id])) {
+        $this->fail($message);
+        return;
+      }
+      $seen_ids[$id] = TRUE;
+    }
+    $this->assertTrue(TRUE, $message);
+  }
+
   /**
    * Tests the "Remember the last selection" functionality.
    */
diff --git a/core/modules/views/tests/src/FunctionalJavascript/BlockExposedFilterAJAXTest.php b/core/modules/views/tests/src/FunctionalJavascript/BlockExposedFilterAJAXTest.php
index 8a8e864106..3fbac566cc 100644
--- a/core/modules/views/tests/src/FunctionalJavascript/BlockExposedFilterAJAXTest.php
+++ b/core/modules/views/tests/src/FunctionalJavascript/BlockExposedFilterAJAXTest.php
@@ -93,4 +93,65 @@ public function testExposedFilteringAndReset() {
     $this->assertSession()->addressEquals('some-path');
   }
 
+  /**
+   * Test that AJAX works with two exposed blocks on the same page.
+   */
+  public function testExposedFilterWithDoubleExposedBlock() {
+    $node = $this->createNode();
+    $block1 = $this->drupalPlaceBlock('views_block:test_block_exposed_ajax-block_1');
+    $block2 = $this->drupalPlaceBlock('views_block:test_block_exposed_ajax-block_1');
+    $this->drupalGet($node->toUrl());
+
+    $page = $this->getSession()->getPage();
+
+    // Ensure that the Content we're testing for is present.
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Page A"]'));
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Page B"]'));
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Article A"]'));
+
+    $form1 = $page->find('css', '#block-' . $block1->id() . ' form');
+    $form1_id = $form1->getAttribute('id');
+    // Filter by page type in the first form.
+    $this->submitForm(['type' => 'page'], t('Apply'), $form1_id);
+    $this->waitForCount(1, 'xpath', '//*[text()="Article A"]');
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Page A"]'));
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Page B"]'));
+    $this->assertCount(1, $page->findAll('xpath', '//*[text()="Article A"]'));
+
+    $form2 = $page->find('css', '#block-' . $block2->id() . ' form');
+    $form2_id = $form2->getAttribute('id');
+    // Filter by page type in the second form.
+    $this->submitForm(['type' => 'page'], t('Apply'), $form2_id);
+    $this->waitForCount(0, 'xpath', '//*[text()="Article A"]');
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Page A"]'));
+    $this->assertCount(2, $page->findAll('xpath', '//*[text()="Page B"]'));
+    $this->assertCount(0, $page->findAll('xpath', '//*[text()="Article A"]'));
+  }
+
+  /**
+   * Looks for the selector and waits for the the count is matched.
+   *
+   * @param int $count
+   *   The count to match.
+   * @param string $selector
+   *   The selector engine name. See ElementInterface::findAll() for the
+   *   supported selectors.
+   * @param string|array $locator
+   *   The selector locator.
+   * @param int $timeout
+   *   (Optional) Timeout in milliseconds, defaults to 10000.
+   *
+   * @return bool
+   *   TRUE if count was matched, FALSE if not.
+   */
+  protected function waitForCount($count, $selector, $locator, $timeout = 10000) {
+    $page = $this->getSession()->getPage();
+
+    $result = $page->waitFor($timeout / 1000, function () use ($page, $count, $selector, $locator) {
+      return count($page->findAll($selector, $locator)) === $count;
+    });
+
+    return $result;
+  }
+
 }
diff --git a/core/modules/views/tests/src/Kernel/Plugin/ExposedFormRenderTest.php b/core/modules/views/tests/src/Kernel/Plugin/ExposedFormRenderTest.php
index a581f8922c..c3287f9af0 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/ExposedFormRenderTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/ExposedFormRenderTest.php
@@ -43,7 +43,7 @@ public function testExposedFormRender() {
     $output = $exposed_form->renderExposedForm();
     $this->setRawContent(\Drupal::service('renderer')->renderRoot($output));
 
-    $this->assertFieldByXpath('//form/@id', Html::cleanCssIdentifier('views-exposed-form-' . $view->storage->id() . '-' . $view->current_display), 'Expected form ID found.');
+    $this->assertFieldByXpath('//form/@id', Html::cleanCssIdentifier('views-exposed-form-' . $view->storage->id() . '-' . $view->current_display . '--2'), 'Expected form ID found.');
 
     $view->setDisplay('page_1');
     $expected_action = $view->display_handler->getUrlInfo()->toString();
diff --git a/core/modules/views/tests/src/Kernel/ViewElementTest.php b/core/modules/views/tests/src/Kernel/ViewElementTest.php
index 186794bd13..df6a92fef1 100644
--- a/core/modules/views/tests/src/Kernel/ViewElementTest.php
+++ b/core/modules/views/tests/src/Kernel/ViewElementTest.php
@@ -127,7 +127,7 @@ public function testViewElementEmbed() {
     $this->setRawContent($renderer->renderRoot($render));
 
     // Ensure that the exposed form is rendered.
-    $this->assertCount(1, $this->xpath('//form[@class="views-exposed-form"]'));
+    $this->assertCount(1, $this->xpath('//form[@class="views-exposed-form views-exposed-form-test-view-embed-embed-2"]'));
   }
 
   /**
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 9e03548ee2..aa99fb0b6c 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -68,6 +68,9 @@ function views_views_pre_render($view) {
         ],
       ],
     ];
+    if ($view->exposed_widgets) {
+      $view->element['#attached']['drupalSettings']['views']['ajaxViews']['views_dom_id:' . $view->dom_id]['view_html_id'] = $view->exposed_widgets['#id'];
+    }
     $view->element['#attached']['library'][] = 'views/views.ajax';
   }
 
