MDL-61138 javascript: stop duplicate custom events firing
authorRyan Wyllie <ryan@moodle.com>
Wed, 21 Mar 2018 07:04:55 +0000 (15:04 +0800)
committerRyan Wyllie <ryan@moodle.com>
Wed, 18 Apr 2018 03:19:37 +0000 (11:19 +0800)
lib/amd/build/custom_interaction_events.min.js
lib/amd/src/custom_interaction_events.js

index dcf7563..8c7b7b1 100644 (file)
Binary files a/lib/amd/build/custom_interaction_events.min.js and b/lib/amd/build/custom_interaction_events.min.js differ
index d0fbb6c..98725b8 100644 (file)
@@ -44,6 +44,10 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         ctrlPageDown: 'cie:ctrlPageDown',
         enter: 'cie:enter',
     };
+    // Static cache of jQuery events that have been handled. This should
+    // only be populated by JavaScript generated events (which will keep it
+    // fairly small).
+    var triggeredEvents = {};
 
     /**
      * Check if the caller has asked for the given event type to be
@@ -77,6 +81,48 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         return (e.shiftKey || e.metaKey || e.altKey || e.ctrlKey);
     };
 
+    /**
+     * Trigger the custom event for the given jQuery event.
+     *
+     * This function will only fire the custom event if one hasn't already been
+     * fired for the jQuery event.
+     *
+     * This is to prevent multiple custom event handlers triggering multiple
+     * custom events for a single jQuery event as it bubbles up the stack.
+     *
+     * @param  {string} eventName The name of the custom event
+     * @param  {event} e          The jQuery event
+     * @return {void}
+     */
+    var triggerEvent = function(eventName, e) {
+        var eventTypeKey = "";
+
+        if (!e.hasOwnProperty('originalEvent')) {
+            // This is a jQuery event generated from JavaScript not a browser event so
+            // we need to build the cache key for the event.
+            eventTypeKey = "" + eventName + e.type + e.timeStamp;
+
+            if (!triggeredEvents.hasOwnProperty(eventTypeKey)) {
+                // If we haven't seen this jQuery event before then fire a custom
+                // event for it and remember the event for later.
+                triggeredEvents[eventTypeKey] = true;
+                $(e.target).trigger(eventName, [{originalEvent: e}]);
+            }
+            return;
+        }
+
+        eventTypeKey = "triggeredCustom_" + eventName;
+        if (!e.originalEvent.hasOwnProperty(eventTypeKey)) {
+            // If this is a jQuery event generated by the browser then set a
+            // property on the original event to track that we've seen it before.
+            // The property is set on the original event because it's the only part
+            // of the jQuery event that is maintained through multiple event handlers.
+            e.originalEvent[eventTypeKey] = true;
+            $(e.target).trigger(eventName, [{originalEvent: e}]);
+            return;
+        }
+    };
+
     /**
      * Register a keyboard event that ignores modifier keys.
      *
@@ -90,7 +136,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         element.off('keydown.' + event).on('keydown.' + event, function(e) {
             if (!isModifierPressed(e)) {
                 if (e.keyCode == keyCode) {
-                    $(e.target).trigger(event, [{originalEvent: e}]);
+                    triggerEvent(event, e);
                 }
             }
         });
@@ -106,12 +152,12 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
      */
     var addActivateListener = function(element) {
         element.off('click.cie.activate').on('click.cie.activate', function(e) {
-            $(e.target).trigger(events.activate, [{originalEvent: e}]);
+            triggerEvent(events.activate, e);
         });
         element.off('keydown.cie.activate').on('keydown.cie.activate', function(e) {
             if (!isModifierPressed(e)) {
                 if (e.keyCode == keyCodes.enter || e.keyCode == keyCodes.space) {
-                    $(e.target).trigger(events.activate, [{originalEvent: e}]);
+                    triggerEvent(events.activate, e);
                 }
             }
         });
@@ -129,7 +175,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         element.off('keydown.cie.keyboardactivate').on('keydown.cie.keyboardactivate', function(e) {
             if (!isModifierPressed(e)) {
                 if (e.keyCode == keyCodes.enter || e.keyCode == keyCodes.space) {
-                    $(e.target).trigger(events.keyboardActivate, [{originalEvent: e}]);
+                    triggerEvent(events.keyboardActivate, e);
                 }
             }
         });
@@ -250,7 +296,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         element.off('scroll.cie.scrollTop').on('scroll.cie.scrollTop', function(e) {
             var scrollTop = element.scrollTop();
             if (scrollTop === 0) {
-                element.trigger(events.scrollTop, [{originalEvent: e}]);
+                triggerEvent(events.scrollTop, e);
             }
         });
     };
@@ -270,7 +316,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
             var scrollHeight = element[0].scrollHeight;
 
             if (scrollTop + innerHeight >= scrollHeight) {
-                element.trigger(events.scrollBottom, [{originalEvent: e}]);
+                triggerEvent(events.scrollBottom, e);
             }
         });
     };
@@ -302,7 +348,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
                     e.preventDefault();
                     e.returnValue = false;
                     // Fire the scroll lock event.
-                    element.trigger(events.scrollLock, [{originalEvent: e}]);
+                    triggerEvent(events.scrollLock, e);
 
                     return false;
                 } else if (up && delta > scrollTop) {
@@ -312,7 +358,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
                     e.preventDefault();
                     e.returnValue = false;
                     // Fire the scroll lock event.
-                    element.trigger(events.scrollLock, [{originalEvent: e}]);
+                    triggerEvent(events.scrollLock, e);
 
                     return false;
                 }
@@ -333,7 +379,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         element.off('keydown.cie.ctrlpageup').on('keydown.cie.ctrlpageup', function(e) {
             if (e.ctrlKey) {
                 if (e.keyCode == keyCodes.pageUp) {
-                    $(e.target).trigger(events.ctrlPageUp, [{originalEvent: e}]);
+                    triggerEvent(events.ctrlPageUp, e);
                 }
             }
         });
@@ -351,7 +397,7 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
         element.off('keydown.cie.ctrlpagedown').on('keydown.cie.ctrlpagedown', function(e) {
             if (e.ctrlKey) {
                 if (e.keyCode == keyCodes.pageDown) {
-                    $(e.target).trigger(events.ctrlPageDown, [{originalEvent: e}]);
+                    triggerEvent(events.ctrlPageDown, e);
                 }
             }
         });