mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Move Safari onClick hack into ReactDOMComponent (#8189)
This is already where we trap non-bubbling events. We also already branch on the tag type so we know if it is interactive or not. This will let us get rid of the didPutListener and willDeleteListener abstractions. I use a simple onclick = emptyFunction to avoid the need for a bookkeeping map.
This commit is contained in:
committed by
GitHub
parent
09ce083c4c
commit
b20e3afbf5
@@ -12,9 +12,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var EventListener = require('EventListener');
|
||||
var EventPropagators = require('EventPropagators');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var SyntheticAnimationEvent = require('SyntheticAnimationEvent');
|
||||
var SyntheticClipboardEvent = require('SyntheticClipboardEvent');
|
||||
var SyntheticEvent = require('SyntheticEvent');
|
||||
@@ -27,7 +25,6 @@ var SyntheticTransitionEvent = require('SyntheticTransitionEvent');
|
||||
var SyntheticUIEvent = require('SyntheticUIEvent');
|
||||
var SyntheticWheelEvent = require('SyntheticWheelEvent');
|
||||
|
||||
var emptyFunction = require('emptyFunction');
|
||||
var getEventCharCode = require('getEventCharCode');
|
||||
var invariant = require('invariant');
|
||||
|
||||
@@ -141,14 +138,6 @@ var topLevelEventsToDispatchConfig: {[key: TopLevelTypes]: DispatchConfig} = {};
|
||||
topLevelEventsToDispatchConfig[topEvent] = type;
|
||||
});
|
||||
|
||||
var onClickListeners = {};
|
||||
|
||||
function getDictionaryKey(inst: ReactInstance): string {
|
||||
// Prevents V8 performance issue:
|
||||
// https://github.com/facebook/react/pull/7232
|
||||
return '.' + inst._rootNodeID;
|
||||
}
|
||||
|
||||
function isInteractive(tag) {
|
||||
return (
|
||||
tag === 'button' || tag === 'input' ||
|
||||
@@ -304,40 +293,6 @@ var SimpleEventPlugin: PluginModule<MouseEvent> = {
|
||||
return event;
|
||||
},
|
||||
|
||||
didPutListener: function(
|
||||
inst: ReactInstance,
|
||||
registrationName: string,
|
||||
listener: () => void,
|
||||
): void {
|
||||
// Mobile Safari does not fire properly bubble click events on
|
||||
// non-interactive elements, which means delegated click listeners do not
|
||||
// fire. The workaround for this bug involves attaching an empty click
|
||||
// listener on the target node.
|
||||
// http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
|
||||
if (registrationName === 'onClick' && !isInteractive(inst._tag)) {
|
||||
var key = getDictionaryKey(inst);
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
if (!onClickListeners[key]) {
|
||||
onClickListeners[key] = EventListener.listen(
|
||||
node,
|
||||
'click',
|
||||
emptyFunction
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
willDeleteListener: function(
|
||||
inst: ReactInstance,
|
||||
registrationName: string,
|
||||
): void {
|
||||
if (registrationName === 'onClick' && !isInteractive(inst._tag)) {
|
||||
var key = getDictionaryKey(inst);
|
||||
onClickListeners[key].remove();
|
||||
delete onClickListeners[key];
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = SimpleEventPlugin;
|
||||
|
||||
@@ -313,6 +313,20 @@ function trackInputValue() {
|
||||
inputValueTracking.track(this);
|
||||
}
|
||||
|
||||
function trapClickOnNonInteractiveElement() {
|
||||
// Mobile Safari does not fire properly bubble click events on
|
||||
// non-interactive elements, which means delegated click listeners do not
|
||||
// fire. The workaround for this bug involves attaching an empty click
|
||||
// listener on the target node.
|
||||
// http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
|
||||
// Just set it using the onclick property so that we don't have to manage any
|
||||
// bookkeeping for it. Not sure if we need to clear it when the listener is
|
||||
// removed.
|
||||
// TODO: Only do this for the relevant Safaris maybe?
|
||||
var node = getNode(this);
|
||||
node.onclick = emptyFunction;
|
||||
}
|
||||
|
||||
function trapBubbledEventsLocal() {
|
||||
var inst = this;
|
||||
// If a component renders to null or if another component fatals and causes
|
||||
@@ -711,6 +725,14 @@ ReactDOMComponent.Mixin = {
|
||||
this
|
||||
);
|
||||
break;
|
||||
default:
|
||||
if (typeof props.onClick === 'function') {
|
||||
transaction.getReactMountReady().enqueue(
|
||||
trapClickOnNonInteractiveElement,
|
||||
this
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return mountImage;
|
||||
@@ -911,6 +933,15 @@ ReactDOMComponent.Mixin = {
|
||||
lastProps = ReactDOMTextarea.getHostProps(this, lastProps);
|
||||
nextProps = ReactDOMTextarea.getHostProps(this, nextProps);
|
||||
break;
|
||||
default:
|
||||
if (typeof lastProps.onClick !== 'function' &&
|
||||
typeof nextProps.onClick === 'function') {
|
||||
transaction.getReactMountReady().enqueue(
|
||||
trapClickOnNonInteractiveElement,
|
||||
this
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
assertValidProps(this, nextProps);
|
||||
|
||||
Reference in New Issue
Block a user