diff --git a/core/modules/ckeditor/js/ckeditor.js b/core/modules/ckeditor/js/ckeditor.js
index 497393f..6a20cd2 100644
--- a/core/modules/ckeditor/js/ckeditor.js
+++ b/core/modules/ckeditor/js/ckeditor.js
@@ -273,6 +273,20 @@
     }
   });
 
+  // Redirect on hash change when the original hash has an associated CKEditor.
+  function redirectTextareaFragmentToCKEditorInstance() {
+    var hash = location.hash.substr(1);
+    var element = document.getElementById(hash);
+    if (element) {
+      var editor = CKEDITOR.dom.element.get(element).getEditor();
+      if (editor) {
+        var id = editor.container.getAttribute('id');
+        location.hash = '#' + id;
+      }
+    }
+  }
+  $(window).on('hashchange', redirectTextareaFragmentToCKEditorInstance);
+
   // Set the CKEditor cache-busting string to the same value as Drupal.
   CKEDITOR.timestamp = drupalSettings.ckeditor.timestamp;
 
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php
new file mode 100644
index 0000000..3c529c7
--- /dev/null
+++ b/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Drupal\Tests\ckeditor\FunctionalJavascript;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\editor\Entity\Editor;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\filter\Entity\FilterFormat;
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Tests the integration of CKEditor.
+ *
+ * @group ckeditor
+ */
+class CKEditorIntegrationTest extends JavascriptTestBase {
+
+  /**
+   * The account.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $account;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node', 'ckeditor', 'filter'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create a text format and associate CKEditor.
+    $filtered_html_format = FilterFormat::create([
+      'format' => 'filtered_html',
+      'name' => 'Filtered HTML',
+      'weight' => 0,
+    ]);
+    $filtered_html_format->save();
+
+    Editor::create([
+      'format' => 'filtered_html',
+      'editor' => 'ckeditor',
+    ])->save();
+
+    // Create a node type for testing.
+    NodeType::create(['type' => 'page', 'name' => 'page'])->save();
+
+    $field_storage = FieldStorageConfig::loadByName('node', 'body');
+
+    // Create a body field instance for the 'page' node type.
+    FieldConfig::create([
+      'field_storage' => $field_storage,
+      'bundle' => 'page',
+      'label' => 'Body',
+      'settings' => ['display_summary' => TRUE],
+      'required' => TRUE,
+    ])->save();
+
+    // Assign widget settings for the 'default' form mode.
+    EntityFormDisplay::create([
+      'targetEntityType' => 'node',
+      'bundle' => 'page',
+      'mode' => 'default',
+      'status' => TRUE,
+    ])->setComponent('body', ['type' => 'text_textarea_with_summary'])
+      ->save();
+
+    $this->account = $this->drupalCreateUser([
+      'administer nodes',
+      'create page content',
+      'use text format filtered_html',
+    ]);
+    $this->drupalLogin($this->account);
+  }
+
+  /**
+   * Tests if the fragment link to a textarea works with CKEditor enabled.
+   */
+  public function testFragmentLink() {
+    $session = $this->getSession();
+    $web_assert = $this->assertSession();
+    $ckeditor_id = '#cke_edit-body-0-value';
+
+    $this->drupalGet('node/add/page');
+
+    $session->getPage();
+
+    // Add a bottom margin to the title field to be sure the body field is not
+    // visible. PhantomJS runs with a resolution of 1024x768px.
+    $session->executeScript("document.getElementById('edit-title-0-value').style.marginBottom = '800px';");
+
+    // Check that the CKEditor-enabled body field is currently not visible in
+    // the viewport.
+    $web_assert->assertNotVisibleInViewport('css', $ckeditor_id, 'topLeft', 'CKEditor-enabled body field is visible.');
+
+    // Trigger a hash change with as target the hidden textarea.
+    $session->executeScript("location.hash = '#edit-body-0-value';");
+
+    // Check that the CKEditor-enabled body field is visible in the viewport.
+    $web_assert->assertVisibleInViewport('css', $ckeditor_id, 'topLeft', 'CKEditor-enabled body field is not visible.');
+  }
+
+}
diff --git a/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php b/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
new file mode 100644
index 0000000..4e83090
--- /dev/null
+++ b/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace Drupal\Tests\inline_form_errors\FunctionalJavascript;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\editor\Entity\Editor;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\filter\Entity\FilterFormat;
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Tests the inline errors fragment link to a CKEditor-enabled textarea.
+ *
+ * @group ckeditor
+ */
+class FormErrorHandlerCKEditorTest extends JavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node', 'ckeditor', 'inline_form_errors', 'filter'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create a text format and associate CKEditor.
+    $filtered_html_format = FilterFormat::create([
+      'format' => 'filtered_html',
+      'name' => 'Filtered HTML',
+      'weight' => 0,
+    ]);
+    $filtered_html_format->save();
+
+    Editor::create([
+      'format' => 'filtered_html',
+      'editor' => 'ckeditor',
+    ])->save();
+
+    // Create a node type for testing.
+    NodeType::create(['type' => 'page', 'name' => 'page'])->save();
+
+    $field_storage = FieldStorageConfig::loadByName('node', 'body');
+
+    // Create a body field instance for the 'page' node type.
+    FieldConfig::create([
+      'field_storage' => $field_storage,
+      'bundle' => 'page',
+      'label' => 'Body',
+      'settings' => ['display_summary' => TRUE],
+      'required' => TRUE,
+    ])->save();
+
+    // Assign widget settings for the 'default' form mode.
+    EntityFormDisplay::create([
+      'targetEntityType' => 'node',
+      'bundle' => 'page',
+      'mode' => 'default',
+      'status' => TRUE,
+    ])->setComponent('body', ['type' => 'text_textarea_with_summary'])
+      ->save();
+
+    $account = $this->drupalCreateUser([
+      'administer nodes',
+      'create page content',
+      'use text format filtered_html',
+    ]);
+    $this->drupalLogin($account);
+  }
+
+  /**
+   * Tests if the fragment link to a textarea works with CKEditor enabled.
+   */
+  public function testFragmentLink() {
+    $session = $this->getSession();
+
+    $this->drupalGet('node/add/page');
+
+    $page = $this->getSession()->getPage();
+
+    // Only enter a title in the node add form and leave the body field empty.
+    $edit = ['edit-title-0-value' => 'Test inline form error with CKEditor'];
+
+    $this->submitForm($edit, 'Save and publish');
+
+    // Add a bottom margin to the title field to be sure the body field is not
+    // visible. PhantomJS runs with a resolution of 1024x768px.
+    $session->executeScript("document.getElementById('edit-title-0-value').style.marginBottom = '800px';");
+
+    // Javascript to test if top of the CKEditor-enabled body field is visible
+    // in the viewport. The NodeElement::isVisible() method doesn't take the
+    // viewport into account.
+    $visibility_check_javascript = <<<JS
+    (function(){
+        var w = window,
+        d = document,
+        e = d.documentElement,
+        el = d.getElementById('cke_edit-body-0-value'),
+        r = el.getBoundingClientRect();
+
+        return (
+          r.top >= 0 &&
+          r.top < (w.innerHeight || e.clientHeight)
+        );
+    }());
+JS;
+
+    // Check that the CKEditor-enabled body field is currently not visible in
+    // the viewport.
+    $this->assertFalse($session->evaluateScript($visibility_check_javascript), 'CKEditor-enabled body field is not visible.');
+
+    // Check if we can find the error fragment link within the errors summary
+    // message.
+    $errors_link = $page->find('css', '.messages--error a[href=\#edit-body-0-value]');
+    $this->assertTrue($errors_link->isVisible(), 'Error fragment link is visible.');
+
+    $errors_link->click();
+
+    // Check that the CKEditor-enabled body field is visible in the viewport.
+    $this->assertTrue($session->evaluateScript($visibility_check_javascript), 'CKEditor-enabled body field is visible.');
+  }
+
+}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
index 8d3ff47..26e4278 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
@@ -2,6 +2,10 @@
 
 namespace Drupal\FunctionalJavascriptTests;
 
+use Behat\Mink\Element\NodeElement;
+use Behat\Mink\Exception\ElementHtmlException;
+use Behat\Mink\Exception\ElementNotFoundException;
+use Behat\Mink\Exception\UnsupportedDriverActionException;
 use Drupal\Tests\WebAssert;
 
 /**
@@ -28,4 +32,199 @@ public function assertWaitOnAjaxRequest($timeout = 10000, $message = 'Unable to
     }
   }
 
+  /**
+   * Test that a node, or it's specific corner, is visible in the viewport.
+   *
+   * Note: Always set the viewport size. This can be done with a PhantomJS
+   * startup parameter or in your test with \Behat\Mink\Session->resizeWindow().
+   * Drupal CI Javascript tests by default use a viewport of 1024x768px.
+   *
+   * @param string $selector_type
+   *   The element selector type (CSS, XPath).
+   * @param string|array $selector
+   *   The element selector. Note: the first found element is used.
+   * @param bool|string $corner
+   *   (Optional) The corner to test:
+   *   topLeft, topRight, bottomRight, bottomLeft.
+   *   Or FALSE to check the complete element (default).
+   * @param string $message
+   *   (optional) A message for the exception.
+   *
+   * @throws \Behat\Mink\Exception\ElementHtmlException
+   *   When the element doesn't exist.
+   * @throws \Behat\Mink\Exception\ElementNotFoundException
+   *   When the element is not visible in the viewport.
+   */
+  public function assertVisibleInViewport($selector_type, $selector, $corner = FALSE, $message = 'Element is not visible in the viewport.') {
+    $node = $this->session->getPage()->find($selector_type, $selector);
+    if ($node === NULL) {
+      if (is_array($selector)) {
+        $selector = implode(' ', $selector);
+      }
+      throw new ElementNotFoundException($this->session->getDriver(), 'element', $selector_type, $selector);
+    }
+
+    // Check if the node is visible on the page, which is a prerequisite of
+    // being visible in the viewport.
+    if (!$node->isVisible()) {
+      throw new ElementHtmlException($message, $this->session->getDriver(), $node);
+    }
+
+    $result = $this->checkNodeVisibilityInViewport($node, $corner);
+
+    if (!$result) {
+      throw new ElementHtmlException($message, $this->session->getDriver(), $node);
+    }
+  }
+
+  /**
+   * Test that a node, or its specific corner, is not visible in the viewport.
+   *
+   * Note: the node should exist in the page, otherwise this assertion fails.
+   *
+   * @param string $selector_type
+   *   The element selector type (CSS, XPath).
+   * @param string|array $selector
+   *   The element selector. Note: the first found element is used.
+   * @param bool|string $corner
+   *   (Optional) Corner to test: topLeft, topRight, bottomRight, bottomLeft.
+   *   Or FALSE to check the complete element (default).
+   * @param string $message
+   *   (optional) A message for the exception.
+   *
+   * @throws \Behat\Mink\Exception\ElementHtmlException
+   *   When the element doesn't exist.
+   * @throws \Behat\Mink\Exception\ElementNotFoundException
+   *   When the element is not visible in the viewport.
+   *
+   * @see \Drupal\FunctionalJavascriptTests\JSWebAssert::assertVisibleInViewport()
+   */
+  public function assertNotVisibleInViewport($selector_type, $selector, $corner = FALSE, $message = 'Element is visible in the viewport.') {
+    $node = $this->session->getPage()->find($selector_type, $selector);
+    if ($node === NULL) {
+      if (is_array($selector)) {
+        $selector = implode(' ', $selector);
+      }
+      throw new ElementNotFoundException($this->session->getDriver(), 'element', $selector_type, $selector);
+    }
+
+    $result = $this->checkNodeVisibilityInViewport($node, $corner);
+
+    if ($result) {
+      throw new ElementHtmlException($message, $this->session->getDriver(), $node);
+    }
+  }
+
+  /**
+   * Check the visibility of a node, or it's specific corner.
+   *
+   * @param \Behat\Mink\Element\NodeElement $node
+   *   A valid node.
+   * @param bool|string $corner
+   *   (Optional) Corner to test: topLeft, topRight, bottomRight, bottomLeft.
+   *   Or FALSE to check the complete element (default).
+   *
+   * @return bool
+   *   Returns TRUE if the node is visible in the viewport, FALSE otherwise.
+   *
+   * @throws \Behat\Mink\Exception\UnsupportedDriverActionException
+   *   When an invalid corner specification is given.
+   */
+  private function checkNodeVisibilityInViewport(NodeElement $node, $corner = FALSE) {
+    $xpath = $node->getXpath();
+
+    // Build the Javascript to test if the complete element or a specific corner
+    // is in the viewport.
+    switch ($corner) {
+      case 'topLeft':
+        $test_javascript_function = <<<JS
+          function t(r, lx, ly) {
+            return (
+              r.top >= 0 &&
+              r.top <= ly &&
+              r.left >= 0 &&
+              r.left <= lx
+            )
+          }
+JS;
+        break;
+
+      case 'topRight':
+        $test_javascript_function = <<<JS
+          function t(r, lx, ly) {
+            return (
+              r.top >= 0 &&
+              r.top <= ly &&
+              r.right >= 0 &&
+              r.right <= lx
+            );
+          }
+JS;
+        break;
+
+      case 'bottomRight':
+        $test_javascript_function = <<<JS
+          function t(r, lx, ly) {
+            return (
+              r.bottom >= 0 &&
+              r.bottom <= ly &&
+              r.right >= 0 &&
+              r.right <= lx
+            );
+          }
+JS;
+        break;
+
+      case 'bottomLeft':
+        $test_javascript_function = <<<JS
+          function t(r, lx, ly) {
+            return (
+              r.bottom >= 0 &&
+              r.bottom <= ly &&
+              r.left >= 0 &&
+              r.left <= lx
+            );
+          }
+JS;
+        break;
+
+      case FALSE:
+        $test_javascript_function = <<<JS
+          function t(r, lx, ly) {
+            return (
+              r.top >= 0 &&
+              r.left >= 0 &&
+              r.bottom <= ly &&
+              r.right <= lx
+            );
+          }
+JS;
+        break;
+
+      // Throw an exception if an invalid corner parameter is given.
+      default:
+        throw new UnsupportedDriverActionException($corner, $this->session->getDriver());
+    }
+
+    // Build the full Javascript test. The shared logic gets the corner
+    // specific test logic injected.
+    $full_javascript_visibility_test = <<<JS
+      (function(t){
+        var w = window,
+        d = document,
+        e = d.documentElement,
+        n = d.evaluate("$xpath", d, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue,
+        r = n.getBoundingClientRect(),
+        lx = (w.innerWidth || e.clientWidth),
+        ly = (w.innerHeight || e.clientHeight);
+
+        return t(r, lx, ly);
+      }($test_javascript_function));
+JS;
+
+    // Check the visibility by injecting and executing the full Javascript test
+    // script in the page.
+    return $this->session->evaluateScript($full_javascript_visibility_test);
+  }
+
 }
