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
},