-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trap click events for portal root #11927
Conversation
@@ -1125,6 +1126,7 @@ function createPortal( | |||
isValidContainer(container), | |||
'Target container is not a DOM element.', | |||
); | |||
trapClickOnNonInteractiveElement(((container: any): HTMLElement)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not sure we want to introduce side effects there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there another place to perform DOM-specific effects when a portal is mounted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like appendToContainer
in the renderer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do it in appendChildToContainer
then we'll also be trapping click events on other containers, I guess that's fine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm maybe? Idk :-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createPortal shouldn't have side effects
@@ -760,7 +761,11 @@ const DOMRenderer = ReactFiberReconciler({ | |||
): void { | |||
if (container.nodeType === COMMENT_NODE) { | |||
(container.parentNode: any).insertBefore(child, container); | |||
trapClickOnNonInteractiveElement( | |||
((container.parentNode: any): HTMLElement), | |||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gaearon is using a comment node as a mount point supported for portals as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth supporting for consistency I guess
879cc35
to
7e8a4dc
Compare
} else { | ||
trapClickOnNonInteractiveElement(((container: any): HTMLElement)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if it already has onclick
? Is it expected that rendering into it overrides onclick
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess if it already has onclick
we don't need to trap click events, so I'll wrap this in a null check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do that in trapClickOnNonInteractiveElement
itself then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's what I was doing :) my only hesitation is that trapClickOnNonInteractiveElement
is also called on elements that React controls (which shouldn't have onclick
making the check redundant in most cases).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I guess you're right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way do we do this regardless of whether we're mobile safari? Is that good? Should we check the browser somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we do it regardless of the browser. There's an existing TODO for adding browser detection. It might be a good time to address that, assuming browser detection is fast/reliable enough.
Hey Brandon! Are there any next steps I can help figure out to move this along? The TODO you referenced for only applying this behavior in Safari also has an issue tracking it (#12989). Should we focus on that first? |
7e8a4dc
to
11687a3
Compare
I don't understand this fix. In the existing code we set Here, we set |
It's been awhile, but see this JSFiddle from the issue @gaearon. Attaching an event listener to the container allows the event to bubble 🤷♂️ |
We need to have a rational explanation first. 😄 |
Is "because iOS Safari" rational enough? 😅 I'll have to dig into this again to make any sense of it. |
Scare tactics. |
It does work though. WHY |
Seems fine. I added a comment for why this works. |
ReactDOM: size: 0.0%, gzip: 🔺+0.1% Details of bundled changes.Comparing: 0da5102...4893ae2 react-dom
Generated by 🚫 dangerJS |
Fixes facebook#13777 As part of facebook#11927 we introduced a regression by adding onclick handler to the React root as well. This causes the whole React tree to flash when tapped on iOS devices (for reasons I outlined in facebook#12989 (comment)). To fix this, we should only apply onclick listeners to portal roots. I verified that this fix indeed worked by checkout out our DOM fixtures and added regression tests as well. Strangely, I had to make changes to the DOM fixtures to see the behavior in the first place. This seems to be caused by our normal sites being bigger than the viewport: ![](http://cl.ly/3f18f8b85e91/Screen%20Recording%202018-10-05%20at%2001.32%20AM.gif) An alternative fix would be to add a third parameter to `appendChildToContainer` based on the tag of the parent fiber. Although I think relying on the `_reactRootContainer` property that we set on the element is less intrusive.
Fixes facebook#13777 As part of facebook#11927 we introduced a regression by adding onclick handler to the React root. This causes the whole React tree to flash when tapped on iOS devices (for reasons I outlined in facebook#12989 (comment)). To fix this, we should only apply onclick listeners to portal roots. I verified that this fix indeed worked by checkout out our DOM fixtures and added regression tests as well. Strangely, I had to make changes to the DOM fixtures to see the behavior in the first place. This seems to be caused by our normal sites being bigger than the viewport: ![](http://cl.ly/3f18f8b85e91/Screen%20Recording%202018-10-05%20at%2001.32%20AM.gif) An alternative fix would be to add a third parameter to `appendChildToContainer` based on the tag of the parent fiber. Although I think relying on the `_reactRootContainer` property that we set on the element is less intrusive.
Fixes #13777 As part of #11927 we introduced a regression by adding onclick handler to the React root. This causes the whole React tree to flash when tapped on iOS devices (for reasons I outlined in #12989 (comment)). To fix this, we should only apply onclick listeners to portal roots. I verified that my proposed fix indeed works by checking out our DOM fixtures and adding regression tests. Strangely, I had to make changes to the DOM fixtures to see the behavior in the first place. This seems to be caused by our normal sites (and thus their React root) being bigger than the viewport: ![](http://cl.ly/3f18f8b85e91/Screen%20Recording%202018-10-05%20at%2001.32%20AM.gif) An alternative approach to finding out if we're appending to a React root would be to add a third parameter to `appendChildToContainer` based on the tag of the parent fiber.
Fixes facebook#13777 As part of facebook#11927 we introduced a regression by adding onclick handler to the React root. This causes the whole React tree to flash when tapped on iOS devices (for reasons I outlined in facebook#12989 (comment)). To fix this, we should only apply onclick listeners to portal roots. I verified that my proposed fix indeed works by checking out our DOM fixtures and adding regression tests. Strangely, I had to make changes to the DOM fixtures to see the behavior in the first place. This seems to be caused by our normal sites (and thus their React root) being bigger than the viewport: ![](http://cl.ly/3f18f8b85e91/Screen%20Recording%202018-10-05%20at%2001.32%20AM.gif) An alternative approach to finding out if we're appending to a React root would be to add a third parameter to `appendChildToContainer` based on the tag of the parent fiber.
Fixes facebook#13777 As part of facebook#11927 we introduced a regression by adding onclick handler to the React root. This causes the whole React tree to flash when tapped on iOS devices (for reasons I outlined in facebook#12989 (comment)). To fix this, we should only apply onclick listeners to portal roots. I verified that my proposed fix indeed works by checking out our DOM fixtures and adding regression tests. Strangely, I had to make changes to the DOM fixtures to see the behavior in the first place. This seems to be caused by our normal sites (and thus their React root) being bigger than the viewport: ![](http://cl.ly/3f18f8b85e91/Screen%20Recording%202018-10-05%20at%2001.32%20AM.gif) An alternative approach to finding out if we're appending to a React root would be to add a third parameter to `appendChildToContainer` based on the tag of the parent fiber.
This event retarget was originally added in order to work around a problem with react event propagation across the shadow root boundary. This appears to be no longer needed. It is currently resulting in double dispatching to the on click handler. While not yet confirmed, it is possible that the underlying issue was fixed with facebook/react#11927 which was released in react-dom v16.5.0.
Should resolve #11918
Still need to run the build in iOS Safari to verify, but I'm 90% sure it will since an empty click handler fixed it in the JSFiddle provided for the issue.