Open rmdort opened 7 years ago
Could you describe your issue in more detail? Im using this library in an iframe and it work just fine (within iframe ofc)
Here is a plunkr demo https://plnkr.co/edit/1DwrgrAqkakmGMamJrXr?p=preview
When you click inside the iframe, handleClickOutside
doesnt get called.
https://www.evernote.com/l/ABot4Su8DvBOSruUVClLgGWoUmq3Aywe5Aw
Note: I am using react-iframe-component - https://github.com/ryanseddon/react-frame-component
This is how browsers work: parent pages do not get notified of clicks inside iframes because those are literally different pages with different security domains, and knowing anything about what happens inside of them means leaking information that can be used for exploits.
Aside from obviously not using iframes (except for maybe editor live-views, they're almost never a good thing), the only signal you get when you click inside the iframe is that the parent document is blurred. However, as the parent can get blurred for a variety of reasons (for instance, clicking outside the browser) that is not a signal that can be used to determine that an outside click inside the same document (as far as the user experiences documents) occurs.
What you can do, if you control both the parent and the iframed content, and you absolutely need this, is write your some code that makes sure that your parent page and iframed page know how to communicate using postMessage
or the like, so that iframed page(s) can signal "something happened inside of me that you wanted to hear about", and then make the parent manually trigger the outside click when it hears about something that you deem should trigger outside-click behaviour.
Aside from obviously not using iframes (except for maybe editor live-views, they're almost never a good thing)
actually iframes are still the safest bet for installable 3rd party widgets too
I think @rmdort might have something else on mind - I think he might be in control of both parent document and iframe and he renders into the iframe from the parent document, so the outside click handler gets registered at the moment in the parent document, while he wants it to be registered inside. Is that right, @rmdort ?
@Andarist Yes. We are using iframes for widgets. I will have control of the parent page as the Javascript to render the widget has to be in the parent page.
@Pomax I will have multiple onclickOutside wrapped react components in the Widget. So even if i use postMessage or add a click
listener on the parent page, how do i get the correct handleClickOutside
handler to call ?
Since you are binding all touch, mousedown
events on the document
, why cant i instead attach the events to IFRAME.documentElement
. Wouldnt it work?
Cant we expose the document
prop so i can pass any element for binding ?
the postmessage design is fully up to you, so one way would be to make sure all your widgets post a message that is JSON serialized data like:
JSON.stringify({
type: "mynamespace::clickedOnWidget",
source: "iframe identifier of some sort that the iframe knows about because you passed it in through the iframe's href as a query argument"
})
That way the parent document can check incoming post messages, and if it's the right type, it can simulate a click
event with mouse coordinates (or touch coordiantes) that correspond to the iframe's document coordinates and no additional work on your end would be required: the click event would get seen by the HOC and because the source is not whatever menu is open right now, that menu will close.
(you can also do source binding by adding a negotiation step to the postmessage communication system, where you bootstrap your iframe with a one-time-pad password as url argument that it can send back to the parent so that the parent can go "I just got a postmessage from source X that uses the right type, and uses the random password I made up for it: I will add this soucrce to my list of whitelisted sources and now I will allow it to send "real" messages to me")
He would still have to detect somehow clicks inside the iframe, but outside of the wrapped component (the one in the iframe) - I think thats why configurable documentElement is requested here.
the idea is that the iframe listens for clicks at its own document
level, then sends a postMessage signal to the parent that "a click has occurred inside of me", so that the parent can then dispatch an artificial click event on its own document
object, which'll automatically trigger the outside click handler.
If a document needs to do anything based on something happening inside of an iframe embedded in the document, then a mutually agreed communication channel needs to be set up, and agreed upon signals need to be sent from one to the other (you could probably even use a websocket connection, although that's a little heavier-handed than just using a decent set of messages through postmessage)
I am not sure we are on the same page here.
In my example, the iframe is not listening for click
in the document
level. Instead the parent page is listening for clicks. The plugin is attaching event handlers to the parent document
.
Instead if the plugin attaches click
event to iframe's document
, it will all just work.
Note: I don't want to trigger handleClickOutside when the parent page is clicked. I am expecting to trigger the handler when user clicks on the iframe. ie anywhere outside the element, but still inside the iframe
No, we're talking about the same thing: if you have a setup with page X containing an iFrame Y, then for security reasons X is never notified of events originating in Y. When you click anywhere inside the clientRect for Y, Y sees that click and is allowed to handle it, but X will never be notified of that click because that would be leaking potentially exploitable information.
Specifically to this HOC, mouse events that occur because of something you do while your cursor (or finger) is over the iFrame will never trigger event handling in the parent (whether you use React or plain Javascript), and the only way around that is for you to intentionally set up a communication channel between X and Y (postmessage, websocket, polling localStorage or sessionStorage, a nasty bit of sneaky ninja Flash for out-of-browser storage, what have you), so that you can make sure that there is code in place that effects events in Y turn into signals that are sent to X for proper handling.
@Pomax I think @rmdort is right here - he describes a little bit different scenario than you.
He has page X which creates components (including the one wrapped in our HOC) and which creates src-less iframe Y - there are no security concerns here and page X can manipulate iframe's Y content and the opposite it true too.
Actually he doesn't want any support for listening to click documents from both documents - he just want simply be able to listen to click events of frame Y where he renders his wrapped component.
At the moment component rendered into frame Y is listening to page's X events.
Yes :+1
Where i have gotten so far is add document
variable to my context, which points to my iframe document
const MyComponent = () => {}
MyComponent.contextTypes = {
document: PropTypes.object
}
var clickConfig = {
getDocument: function (instance) {
return instance.context.document // This can be any user defined function
}
}
export default listensToClickOutside(MyComponent, clickConfig)
And replace document
references in the plugin to getDocument
function, if exists, else fallbacks to window.document. If you are fine with this, i can submit a pull request.
Added a pull request #255 . Can you review it
Is there somethings in react-onclickoutside to support what @Pomax is talking about? Page X have a React component wrapped in react-onclickoutside HOC and a iframe Y, the React component is not inside of the iframe, and obviously the React component did not respond to the onclickoutside event when click on iframe. Do you recommend using some type of communication(postmessage) with the iframe? I am looking for a solution and I found 2 other different solutions like creating a modal-backdrop or adding $(window).on('blur',function() { ... } ); , I am trying to analyze this different options.
If the react component is in an iframe, onclickoutside doesnt really work. You have to click on the parent document to trigger
handleClickOutside
This module binds all click events to
document
. If you can expose thedocument
prop, i can bind events to an iframe document instead. What do you think?