From 90d8c85ec3273a2a428a6f41d2928e96fb801363 Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Wed, 21 Mar 2018 15:04:55 +0800 Subject: [PATCH] MDL-61138 javascript: stop duplicate custom events firing --- .../build/custom_interaction_events.min.js | Bin 3335 -> 3333 bytes lib/amd/src/custom_interaction_events.js | 66 +++++++++++++++--- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/lib/amd/build/custom_interaction_events.min.js b/lib/amd/build/custom_interaction_events.min.js index dcf7563577b424351d75b8dc414885ca0b9740d3..8c7b7b15c95b6da2f2500f4267b86fd1638506a5 100644 GIT binary patch literal 3333 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 diff --git a/lib/amd/src/custom_interaction_events.js b/lib/amd/src/custom_interaction_events.js index d0fbb6cc0c0..98725b8efcb 100644 --- a/lib/amd/src/custom_interaction_events.js +++ b/lib/amd/src/custom_interaction_events.js @@ -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); } } }); -- 2.17.1