From e9389e0b170151ba5cdcc8c7951f28621e43fff2 Mon Sep 17 00:00:00 2001 From: Brandon Dail <Kierkegaurd@gmail.com> Date: Sun, 24 Dec 2017 20:07:44 -0800 Subject: [PATCH 1/3] Trap click events for portal root --- packages/react-dom/src/client/ReactDOM.js | 1 + packages/react-dom/src/client/ReactDOMFiberComponent.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index ea887f774c154..fb426f1839aed 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -577,6 +577,7 @@ function createPortal( isValidContainer(container), 'Target container is not a DOM element.', ); + ReactDOMFiberComponent.trapClickOnNonInteractiveElement(((container: any): HTMLElement)); // TODO: pass ReactDOM portal implementation as third argument return ReactPortal.createPortal(children, container, null, key); } diff --git a/packages/react-dom/src/client/ReactDOMFiberComponent.js b/packages/react-dom/src/client/ReactDOMFiberComponent.js index a0f9bf40366cc..11bba1a1085c4 100644 --- a/packages/react-dom/src/client/ReactDOMFiberComponent.js +++ b/packages/react-dom/src/client/ReactDOMFiberComponent.js @@ -228,7 +228,7 @@ function getOwnerDocumentFromRootContainer( function noop() {} -function trapClickOnNonInteractiveElement(node: HTMLElement) { +export function trapClickOnNonInteractiveElement(node: HTMLElement) { // 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 From 11687a313f4361dbe2c23e434b4a07a8c945f0b6 Mon Sep 17 00:00:00 2001 From: Brandon Dail <Kierkegaurd@gmail.com> Date: Fri, 5 Jan 2018 14:54:30 -0800 Subject: [PATCH 2/3] Trap click event on container in appendChildToContainer --- packages/react-dom/src/client/ReactDOM.js | 1 - packages/react-dom/src/client/ReactDOMHostConfig.js | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index fb426f1839aed..ea887f774c154 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -577,7 +577,6 @@ function createPortal( isValidContainer(container), 'Target container is not a DOM element.', ); - ReactDOMFiberComponent.trapClickOnNonInteractiveElement(((container: any): HTMLElement)); // TODO: pass ReactDOM portal implementation as third argument return ReactPortal.createPortal(children, container, null, key); } diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 8da30eacc43b6..3d3bd6d0fcff5 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -16,6 +16,7 @@ import { updateProperties, diffHydratedProperties, diffHydratedText, + trapClickOnNonInteractiveElement, warnForUnmatchedText, warnForDeletedHydratableElement, warnForDeletedHydratableText, @@ -345,7 +346,11 @@ export function appendChildToContainer( ): void { if (container.nodeType === COMMENT_NODE) { (container.parentNode: any).insertBefore(child, container); + trapClickOnNonInteractiveElement( + ((container.parentNode: any): HTMLElement), + ); } else { + trapClickOnNonInteractiveElement(((container: any): HTMLElement)); container.appendChild(child); } } From 4893ae2781d036dd9d22e81457846ce7a1ef4d56 Mon Sep 17 00:00:00 2001 From: Dan Abramov <dan.abramov@gmail.com> Date: Sat, 18 Aug 2018 02:05:24 +0100 Subject: [PATCH 3/3] Add a comment --- .../src/client/ReactDOMHostConfig.js | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 3d3bd6d0fcff5..e288b6554db21 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -344,14 +344,24 @@ export function appendChildToContainer( container: Container, child: Instance | TextInstance, ): void { + let parentNode; if (container.nodeType === COMMENT_NODE) { - (container.parentNode: any).insertBefore(child, container); - trapClickOnNonInteractiveElement( - ((container.parentNode: any): HTMLElement), - ); + parentNode = (container.parentNode: any); + parentNode.insertBefore(child, container); } else { - trapClickOnNonInteractiveElement(((container: any): HTMLElement)); - container.appendChild(child); + parentNode = container; + parentNode.appendChild(child); + } + // This container might be used for a portal. + // If something inside a portal is clicked, that click should bubble + // through the React tree. However, on Mobile Safari the click would + // never bubble through the *DOM* tree unless an ancestor with onclick + // event exists. So we wouldn't see it and dispatch it. + // This is why we ensure that containers have inline onclick defined. + // https://github.com/facebook/react/issues/11918 + if (parentNode.onclick === null) { + // TODO: This cast may not be sound for SVG, MathML or custom elements. + trapClickOnNonInteractiveElement(((parentNode: any): HTMLElement)); } }