@samitouri / QOS-React-2 / commits / 090e4bbc9a

Revert "Revert #1536"

Formerly "Attach empty onclick listener to each node". This reverts commit 431155d2e231cfb65e236f9289fddf8cea291185.

Ben Alpert committed Feb 23, 2015 at 13:41 UTC 090e4bbc9af6f6ccd5a9d8f279f7bcba7261cd2a
6 files changed +67 -61
src/browser/eventPlugins/DefaultEventPluginOrder.js
+1 -2
@@ -30,8 +30,7 @@ var DefaultEventPluginOrder = [
30 keyOf({ChangeEventPlugin: null}),
31 keyOf({SelectEventPlugin: null}),
32 keyOf({BeforeInputEventPlugin: null}),
33 - keyOf({AnalyticsEventPlugin: null}),
34 - keyOf({MobileSafariClickEventPlugin: null})
33 + keyOf({AnalyticsEventPlugin: null})
34 ];
35
36 module.exports = DefaultEventPluginOrder;
src/browser/eventPlugins/MobileSafariClickEventPlugin.js deleted
-56
@@ -1,56 +0,0 @@
1 -/**
2 - * Copyright 2013-2015, Facebook, Inc.
3 - * All rights reserved.
4 - *
5 - * This source code is licensed under the BSD-style license found in the
6 - * LICENSE file in the root directory of this source tree. An additional grant
7 - * of patent rights can be found in the PATENTS file in the same directory.
8 - *
9 - * @providesModule MobileSafariClickEventPlugin
10 - * @typechecks static-only
11 - */
12 -
13 -'use strict';
14 -
15 -var EventConstants = require('EventConstants');
16 -
17 -var emptyFunction = require('emptyFunction');
18 -
19 -var topLevelTypes = EventConstants.topLevelTypes;
20 -
21 -/**
22 - * Mobile Safari does not fire properly bubble click events on non-interactive
23 - * elements, which means delegated click listeners do not fire. The workaround
24 - * for this bug involves attaching an empty click listener on the target node.
25 - *
26 - * This particular plugin works around the bug by attaching an empty click
27 - * listener on `touchstart` (which does fire on every element).
28 - */
29 -var MobileSafariClickEventPlugin = {
30 -
31 - eventTypes: null,
32 -
33 - /**
34 - * @param {string} topLevelType Record from `EventConstants`.
35 - * @param {DOMEventTarget} topLevelTarget The listening component root node.
36 - * @param {string} topLevelTargetID ID of `topLevelTarget`.
37 - * @param {object} nativeEvent Native browser event.
38 - * @return {*} An accumulation of synthetic events.
39 - * @see {EventPluginHub.extractEvents}
40 - */
41 - extractEvents: function(
42 - topLevelType,
43 - topLevelTarget,
44 - topLevelTargetID,
45 - nativeEvent) {
46 - if (topLevelType === topLevelTypes.topTouchStart) {
47 - var target = nativeEvent.target;
48 - if (target && !target.onclick) {
49 - target.onclick = emptyFunction;
50 - }
51 - }
52 - }
53 -
54 -};
55 -
56 -module.exports = MobileSafariClickEventPlugin;
src/browser/eventPlugins/SimpleEventPlugin.js
+23 -1
@@ -12,8 +12,10 @@
12 'use strict';
13
14 var EventConstants = require('EventConstants');
15 +var EventListener = require('EventListener');
16 var EventPluginUtils = require('EventPluginUtils');
17 var EventPropagators = require('EventPropagators');
18 +var ReactMount = require('ReactMount');
19 var SyntheticClipboardEvent = require('SyntheticClipboardEvent');
20 var SyntheticEvent = require('SyntheticEvent');
21 var SyntheticFocusEvent = require('SyntheticFocusEvent');
@@ -24,8 +26,8 @@ var SyntheticTouchEvent = require('SyntheticTouchEvent');
26 var SyntheticUIEvent = require('SyntheticUIEvent');
27 var SyntheticWheelEvent = require('SyntheticWheelEvent');
28
29 +var emptyFunction = require('emptyFunction');
30 var getEventCharCode = require('getEventCharCode');
28 -
31 var invariant = require('invariant');
32 var keyOf = require('keyOf');
33 var warning = require('warning');
@@ -289,6 +291,9 @@ for (var type in topLevelEventsToDispatchConfig) {
291 topLevelEventsToDispatchConfig[type].dependencies = [type];
292 }
293
294 +var ON_CLICK_KEY = keyOf({onClick: null});
295 +var onClickListeners = {};
296 +
297 var SimpleEventPlugin = {
298
299 eventTypes: eventTypes,
@@ -417,6 +422,23 @@ var SimpleEventPlugin = {
422 );
423 EventPropagators.accumulateTwoPhaseDispatches(event);
424 return event;
425 + },
426 +
427 + didPutListener: function(id, registrationName, listener) {
428 + // Mobile Safari does not fire properly bubble click events on
429 + // non-interactive elements, which means delegated click listeners do not
430 + // fire. The workaround for this bug involves attaching an empty click
431 + // listener on the target node.
432 + if (registrationName === ON_CLICK_KEY) {
433 + var node = ReactMount.getNode(id);
434 + onClickListeners[id] = EventListener.listen(node, 'click', emptyFunction);
435 + }
436 + },
437 +
438 + willDeleteListener: function(id, registrationName) {
439 + if (registrationName === ON_CLICK_KEY) {
440 + onClickListeners[id].remove();
441 + }
442 }
443
444 };
src/browser/ui/ReactDefaultInjection.js
-2
@@ -18,7 +18,6 @@ var DefaultEventPluginOrder = require('DefaultEventPluginOrder');
18 var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
19 var ExecutionEnvironment = require('ExecutionEnvironment');
20 var HTMLDOMPropertyConfig = require('HTMLDOMPropertyConfig');
21 -var MobileSafariClickEventPlugin = require('MobileSafariClickEventPlugin');
21 var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
22 var ReactClass = require('ReactClass');
23 var ReactComponentBrowserEnvironment =
@@ -84,7 +83,6 @@ function inject() {
83 SimpleEventPlugin: SimpleEventPlugin,
84 EnterLeaveEventPlugin: EnterLeaveEventPlugin,
85 ChangeEventPlugin: ChangeEventPlugin,
87 - MobileSafariClickEventPlugin: MobileSafariClickEventPlugin,
86 SelectEventPlugin: SelectEventPlugin,
87 BeforeInputEventPlugin: BeforeInputEventPlugin
88 });
src/browser/ui/__tests__/ReactDOMComponent-test.js
+20
@@ -385,6 +385,26 @@ describe('ReactDOMComponent', function() {
385 'style={{marginRight: spacing + \'em\'}} when using JSX.'
386 );
387 });
388 +
389 + it("should execute custom event plugin listening behavior", function() {
390 + var React = require('React');
391 + var SimpleEventPlugin = require('SimpleEventPlugin');
392 +
393 + SimpleEventPlugin.didPutListener = mocks.getMockFunction();
394 + SimpleEventPlugin.willDeleteListener = mocks.getMockFunction();
395 +
396 + var container = document.createElement('div');
397 + React.render(
398 + <div onClick={() => true} />,
399 + container
400 + );
401 +
402 + expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(1);
403 +
404 + React.unmountComponentAtNode(container);
405 +
406 + expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
407 + });
408 });
409
410 describe('updateComponent', function() {
src/event/EventPluginHub.js
+23
@@ -155,6 +155,12 @@ var EventPluginHub = {
155 var bankForRegistrationName =
156 listenerBank[registrationName] || (listenerBank[registrationName] = {});
157 bankForRegistrationName[id] = listener;
158 +
159 + var PluginModule =
160 + EventPluginRegistry.registrationNameModules[registrationName];
161 + if (PluginModule && PluginModule.didPutListener) {
162 + PluginModule.didPutListener(id, registrationName, listener);
163 + }
164 },
165
166 /**
@@ -174,7 +180,14 @@ var EventPluginHub = {
180 * @param {string} registrationName Name of listener (e.g. `onClick`).
181 */
182 deleteListener: function(id, registrationName) {
183 + var PluginModule =
184 + EventPluginRegistry.registrationNameModules[registrationName];
185 + if (PluginModule && PluginModule.willDeleteListener) {
186 + PluginModule.willDeleteListener(id, registrationName);
187 + }
188 +
189 var bankForRegistrationName = listenerBank[registrationName];
190 + // TODO: This should never be null -- when is it?
191 if (bankForRegistrationName) {
192 delete bankForRegistrationName[id];
193 }
@@ -187,6 +200,16 @@ var EventPluginHub = {
200 */
201 deleteAllListeners: function(id) {
202 for (var registrationName in listenerBank) {
203 + if (!listenerBank[registrationName][id]) {
204 + continue;
205 + }
206 +
207 + var PluginModule =
208 + EventPluginRegistry.registrationNameModules[registrationName];
209 + if (PluginModule && PluginModule.willDeleteListener) {
210 + PluginModule.willDeleteListener(id, registrationName);
211 + }
212 +
213 delete listenerBank[registrationName][id];
214 }
215 },