diff --git a/core/misc/drupal.js b/core/misc/drupal.js
index b1b4125..cbb8870 100644
--- a/core/misc/drupal.js
+++ b/core/misc/drupal.js
@@ -18,6 +18,21 @@ if (window.jQuery) {
   "use strict";
 
   /**
+   * Build the list of behaviors to run.
+   *
+   * @param {String} event
+   *   A string with two possible values: 'attach' or 'detach'.
+   * @param {String} [trigger]
+   *   For a 'detach' event the trigger value detachBehavior was called with.
+   *
+   * @return {Array}
+   *   An ordered array of behavior names.
+   */
+  Drupal.behaviorList = function (event, trigger) {
+    return Object.keys(Drupal.behaviors);
+  };
+
+  /**
    * Custom error type thrown after attach/detach if one or more behaviors failed.
    *
    * @param list
@@ -71,29 +86,33 @@ if (window.jQuery) {
    * enables the reprocessing of given elements, which may be needed on occasion
    * despite the ability to limit behavior attachment to a particular element.)
    *
-   * @param context
+   * @param {DOMElement} context
    *   An element to attach behaviors to. If none is given, the document element
    *   is used.
-   * @param settings
+   * @param {Object} settings
    *   An object containing settings for the current context. If none is given,
    *   the global drupalSettings object is used.
+   * @param {Array} behaviorList
+   *   An list of behavior names to run on the current context.
    */
-  Drupal.attachBehaviors = function (context, settings) {
+  Drupal.attachBehaviors = function (context, settings, behaviorList) {
     context = context || document;
     settings = settings || drupalSettings;
-    var i, errors = [], behaviors = Drupal.behaviors;
+    behaviorList = behaviorList || Drupal.behaviorList('attach');
+    var errors = [];
+    var behaviors = Drupal.behaviors;
     // Execute all of them.
-    for (i in behaviors) {
-      if (behaviors.hasOwnProperty(i) && typeof behaviors[i].attach === 'function') {
+    behaviorList.forEach(function (behaviorName) {
+      if (behaviors.hasOwnProperty(behaviorName) && typeof behaviors[behaviorName].attach === 'function') {
         // Don't stop the execution of behaviors in case of an error.
         try {
-          behaviors[i].attach(context, settings);
+          behaviors[behaviorName].attach(context, settings);
         }
         catch (e) {
-          errors.push({behavior: i, error: e});
+          errors.push({behavior: behaviorName, error: e});
         }
       }
-    }
+    });
     // Once all behaviors have been processed, inform the user about errors.
     if (errors.length) {
       throw new DrupalBehaviorError(errors, 'attach');
@@ -116,13 +135,13 @@ if (window.jQuery) {
    * implementation, i.e. .removeOnce('behaviorName'), to ensure the behavior is
    * detached only from previously processed elements.
    *
-   * @param context
+   * @param {DOMElement} context
    *   An element to detach behaviors from. If none is given, the document element
    *   is used.
-   * @param settings
+   * @param {Object} settings
    *   An object containing settings for the current context. If none given, the
    *   global drupalSettings object is used.
-   * @param trigger
+   * @param {String} trigger
    *   A string containing what's causing the behaviors to be detached. The
    *   possible triggers are:
    *   - unload: (default) The context element is being removed from the DOM.
@@ -140,26 +159,30 @@ if (window.jQuery) {
    *     in them before the form is serialized. The canonical use-case is so
    *     that WYSIWYG editors can update the hidden textarea to which they are
    *     bound.
+   * @param {Array} behaviorList
+   *   An list of behavior names to run on the current context.
    *
    * @see Drupal.attachBehaviors
    */
-  Drupal.detachBehaviors = function (context, settings, trigger) {
+  Drupal.detachBehaviors = function (context, settings, trigger, behaviorList) {
     context = context || document;
     settings = settings || drupalSettings;
     trigger = trigger || 'unload';
-    var i, errors = [], behaviors = Drupal.behaviors;
+    behaviorList = behaviorList || Drupal.behaviorList('detach', trigger);
+    var errors = [];
+    var behaviors = Drupal.behaviors;
     // Execute all of them.
-    for (i in behaviors) {
-      if (behaviors.hasOwnProperty(i) && typeof behaviors[i].detach === 'function') {
+    behaviorList.forEach(function (behaviorName) {
+      if (behaviors.hasOwnProperty(behaviorName) && typeof behaviors[behaviorName].detach === 'function') {
         // Don't stop the execution of behaviors in case of an error.
         try {
-          behaviors[i].detach(context, settings, trigger);
+          behaviors[behaviorName].detach(context, settings, trigger);
         }
         catch (e) {
-          errors.push({behavior: i, error: e});
+          errors.push({behavior: behaviorName, error: e});
         }
       }
-    }
+    });
     // Once all behaviors have been processed, inform the user about errors.
     if (errors.length) {
       throw new DrupalBehaviorError(errors, 'detach:' + trigger);
