You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Event Handler on React Component not invoked when React Component is rendered inside a Web Component #9242
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
A React Component with an Event Handler (for example onClick) is rendered inside a Web Component. When the Component is clicked the Event does not receive the React Component (specified callback is not invoked)
What is the expected behavior?
The event handler should be called.
For testing I have modified getEventTarget.js to return the target from the path property of the nativeEvent (instead of the "original" target from the nativeEvent). With this addition it works -
the Event Handler is called.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
15.4.x and 16.x
I've tested in Chrome, Firefox and Safari. I don't know if it works in previous versions of React (don't think so)
@DeanBDean You are right. We were using Chrome to investigate the event bubbling issue months ago, which gave us access to the event.path property. We are using the webcomponentsjs polyfill to make the shadow dom available in all browsers. Today the polyfill (shadyDom) provides us the composedPath() function, so we should maybe change it to the following:
// If encapsulated in a Web Component use the composed Event pathif(nativeEvent.composedPath&&nativeEvent.composedPath()){returnnativeEvent.composedPath()[0];}
While I do not see a use case for web components in my React applications, I am considering using React in building more complicated web components to be delivered to other platforms (I am thinking for WordPress bloggers; or similar).
Same problem here... Created component with react and want to include it to other sites - similar as iframe. The only way it can work is to retarget events. But I think it realy hurts performace
Does React use event delegation to listen for clicks on React components? If so I imagine the shadow boundary would get in the way of that... Just want to make sure I understand the problem :)
The react-shadow-dom-retarget-events module works, but has a few problems of its own. If I'm correct, it picks up the native events on the shadow dom element, and then directly calls the react component event handlers (onClick, etc.. ) using those native events, instead of synthetic events. I think that because of this, stopPropagation() does not work.
It would be nice to see the React team pick this issue up and provide a proper fix. Shadow dom is gaining momentum! :-)
I've been thinking and maybe it would make sense to attach the global React event handlers to the element returned by getRootNode() instead of to the document?
Reacted by Allen Williamson, Love Mayrhofer, Michael Sholty, Terrance Mutizwa, LevinsDurai, Roman Scher, Matt Walters, Emre Erdoğan, Jonathan Poltak Samosir, Dennis Brotzky and 19 more
Reacted by LevinsDurai, Dennis Brotzky, Ecliptic Rick, Avele and Gilles Yvetot
Reacted by LevinsDurai, Ninh Pham, Dennis Brotzky, Ecliptic Rick, Avele, Malek Boubakri and Gilles Yvetot
Reacted by Malek Boubakri and Gilles Yvetot
Reacted by LevinsDurai, Dennis Brotzky, Ecliptic Rick, Avele, Malek Boubakri and Gilles Yvetot
Is there interest in a fix based on the suggestions by @nilshartmann? I'd be happy to contribute the required changes and new tests if required.
Edit:
If I understand correctly @xastor's suggestion is closely related to #2043, which appears to be the more general approach to fix this.
Reading the lengthy discussion of #8117, which was the last implementation attempt for #2043, I guess that redesigning the event propagation system to support multiple roots is a sprawling/hard to implement change.
Not sure if this is still an issue with anyone; I however found the following addition to your react app entry point will work:
class CustomElement extends HTMLElement {
connectedCallback() {
// setup
const shadow = this.attachShadow({ mode: "open" });
const root = document.createElement("div");
shadow.appendChild(root);
// Making the shadow appear like document
// so react events work as normal
Object.defineProperty(root, "ownerDocument", { value: shadow });
shadow.createElement = (...args) => document.createElement(...args);
ReactDOM.render(<App />, root);
}
}
customElements.define("custom-element", CustomElement);
// DONT FORGET to add <custom-element></custom-element> to your page
The rationale here is:
react event system does work normally
shadow dom re-targeting is the issue
custom re-re-targeting is complex; and does not reliably work with events that do not bubble - example focus and blur
instead of react binding to document it should bind to the shadow. Because as far as css and js events that is the true document root of a shadow dom
react binds event listeners to the ownerDocument property of the root element it renders into. It expects that to be document.
createElement appears to be the only method missing from the shadow element, which react needs
This works with the create-react-app setup.
Any issues one can foresee taking this approach?
Reacted by Patrick Pietens, Erik Johansson, handsomegit, Josh Duff, Shenwei Wang, Jan Pöschko, Adrian Dobre, Millad Dagdoni, 青岚, Tobias Siwonia and 3 more
Reacted by Jamie Duggan, richard-1990, Chris Parton, handsomegit and Tobias Siwonia
Reacted by richard-1990, Jamie Duggan, handsomegit, Shugo Saito and Tobias Siwonia
Later edit: I originally misstated that the problem is in React Portals, but that's not true, the problem is in rc-utils, a dependency of Ant Design System.
However, if the parent container (passed in with getContainer) is created inside the shadowRoot (and it should be, otherwise this is not a problem), relying on the fact that Portal Wrapper does something like parent.appendChild you can rewrite parent.appendChild to also add ownerDocument :)
I switched from react-shadow-dom-retarget-events to renegare fix and all was working well in modern browsers. Unfortunately, this seems to be causing issues with the webcomponents pollyfill in at least edge and IE. I believe it is altering this.ownerDocument on this line and it throws 'SCRIPT65535: Invalid calling object'.
Update: It is the root element that is throwing that error as we are obviously changing the ownerDocument on it to be the shadow object. The pollyfill code expects it to be the document, which is what it is pollyfilling. If you add the pollyfill function being referenced on line 181, all renders fine. However, when you fire an event such as blur, the page dies. Probably a lot of normal assumptions being made that this change violates.
We've built out a fairly small solution for this problem called react-html-element. It's extremely early in its development, so it probably doesn't cover every use case, since there are certainly some we don't know about.
We'll be dogfooding this in our internal projects, but would love anybody that wants to try it out to do so! Please log issues if you find them, as we have a vested interest in it being robust and useful!
Reacted by Anselm Marie, Taras Lukavyi and Corey Gwin
Activity
DeanBDean commentedon Mar 23, 2017
@nilshartmann Should path be composedPath() instead?
https://hayato.io/2016/shadowdomv1/#getting-event-path
By the way, this almost entirely solves an issue I was having where events were not being captured by a web component that encapsulated a ReactDOM.
mgrdevport commentedon Apr 10, 2017
@DeanBDean You are right. We were using Chrome to investigate the event bubbling issue months ago, which gave us access to the event.path property. We are using the webcomponentsjs polyfill to make the shadow dom available in all browsers. Today the polyfill (shadyDom) provides us the composedPath() function, so we should maybe change it to the following:
bthallion commentedon May 21, 2017
I'm having the same problem, is there a workaround for this currently?
gaearon commentedon Oct 4, 2017
We should confirm whether it still happens on master with native web components.
(16.0.0 WC support is borked but I fixed it on master.)
larkintuckerllc commentedon Oct 8, 2017
First, I have also observed this first hand with React 16.0.0 (today). Also confirmed that a fellow opened up a related issue and wrote a working temporary fix, https://www.npmjs.com/package/react-shadow-dom-retarget-events .
While I do not see a use case for web components in my React applications, I am considering using React in building more complicated web components to be delivered to other platforms (I am thinking for WordPress bloggers; or similar).
rytisbac commentedon Oct 13, 2017
Same problem here... Created component with react and want to include it to other sites - similar as iframe. The only way it can work is to retarget events. But I think it realy hurts performace
robdodson commentedon Oct 17, 2017
Does React use event delegation to listen for clicks on React components? If so I imagine the shadow boundary would get in the way of that... Just want to make sure I understand the problem :)
xastor commentedon Nov 22, 2017
The react-shadow-dom-retarget-events module works, but has a few problems of its own. If I'm correct, it picks up the native events on the shadow dom element, and then directly calls the react component event handlers (onClick, etc.. ) using those native events, instead of synthetic events. I think that because of this, stopPropagation() does not work.
It would be nice to see the React team pick this issue up and provide a proper fix. Shadow dom is gaining momentum! :-)
I've been thinking and maybe it would make sense to attach the global React event handlers to the element returned by getRootNode() instead of to the document?
https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode
marionebl commentedon Feb 5, 2018
Is there interest in a fix based on the suggestions by @nilshartmann? I'd be happy to contribute the required changes and new tests if required.
Edit:
If I understand correctly @xastor's suggestion is closely related to #2043, which appears to be the more general approach to fix this.
Reading the lengthy discussion of #8117, which was the last implementation attempt for #2043, I guess that redesigning the event propagation system to support multiple roots is a sprawling/hard to implement change.
opichals commentedon May 29, 2019
Tested successfully using the
event.composePath()in https://github.com/opichals/react/tree/add-event-target-composePath-support based on #9242 (comment)renegare commentedon Sep 23, 2019
Not sure if this is still an issue with anyone; I however found the following addition to your react app entry point will work:
The rationale here is:
ownerDocumentproperty of the root element it renders into. It expects that to be document.createElementappears to be the only method missing from the shadow element, which react needsThis works with the create-react-app setup.
Any issues one can foresee taking this approach?
8 remaining items
JonyE commentedon Dec 17, 2019
@chrisparton1991 u can get window from extension, see here https://gist.github.com/devjin0617/3e8d72d94c1b9e69690717a219644c7a
as about shadow dom: https://www.npmjs.com/package/react-shadow
adrian-dobre commentedon Mar 4, 2020
Later edit: I originally misstated that the problem is in React Portals, but that's not true, the problem is in rc-utils, a dependency of Ant Design System.
Original post
I found one issue with @chrisparton1991 / @renegare 's solution: If you use React Portals the reference to owner document is lost (because it uses document.createElement directly instead of ownerDocument, see: https://github.com/react-component/util/blob/85c296bbf06966c37963da4e8b7ce5dfba367b1a/src/PortalWrapper.js#L102). You could try to avoid Portals usage, unfortunately you could rely on some libs that use Portals (like ant design system - modal).
However, if the parent container (passed in with getContainer) is created inside the shadowRoot (and it should be, otherwise this is not a problem), relying on the fact that Portal Wrapper does something like parent.appendChild you can rewrite parent.appendChild to also add ownerDocument :)
Hopefully this will help somebody having the same problem. Sorry for the somewhat confusing code, I pulled it out from my solution without cleanup.
Maybe PortalWrapper could be updated from:
to
and that would fix the issue
nlrowe commentedon Mar 25, 2020
I switched from react-shadow-dom-retarget-events to renegare fix and all was working well in modern browsers. Unfortunately, this seems to be causing issues with the webcomponents pollyfill in at least edge and IE. I believe it is altering
this.ownerDocumenton this line and it throws 'SCRIPT65535: Invalid calling object'.Update: It is the
rootelement that is throwing that error as we are obviously changing theownerDocumenton it to be the shadow object. The pollyfill code expects it to be the document, which is what it is pollyfilling. If you add the pollyfill function being referenced on line 181, all renders fine. However, when you fire an event such asblur, the page dies. Probably a lot of normal assumptions being made that this change violates.Fix React event handling by changing the ownerDocument of the React r…
stevematney commentedon Apr 6, 2020
We've built out a fairly small solution for this problem called
react-html-element. It's extremely early in its development, so it probably doesn't cover every use case, since there are certainly some we don't know about.We'll be dogfooding this in our internal projects, but would love anybody that wants to try it out to do so! Please log issues if you find them, as we have a vested interest in it being robust and useful!
Add workaround for the issue with react events inside custom HTML com…
gaearon commentedon Aug 17, 2020
This has been fixed in React 17.
Fiddle with 16: https://codesandbox.io/s/elegant-wilson-jirq9
Fiddle with 17: https://codesandbox.io/s/nifty-benz-rflo0
There may still be some corner cases so feel free to file new issues if something doesn't work.
stevematney commentedon Aug 17, 2020
@gaearon was #15894 the fix for this?
gaearon commentedon Aug 17, 2020
No, it was the changes to event delegation (to attach events at the root).
https://reactjs.org/blog/2020/08/10/react-v17-rc.html#changes-to-event-delegation
https://github.com/facebook/react/pulls?q=is%3Apr+author%3Atrueadm+modern+event+is%3Amerged
stevematney commentedon Aug 17, 2020
That looks great! It seems like the perfect fix. Thank you!
Add workaround for the issue with react events inside custom HTML com…
GleanCoder1116 commentedon Mar 6, 2023
升级react 18吧
kudorgyozo commentedon Jun 28, 2023
Is this fixed?
tozz commentedon Jun 29, 2023
@kudorgyozo If you look five comments up.