-
Notifications
You must be signed in to change notification settings - Fork 88
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
fix: iframe click detection #222
fix: iframe click detection #222
Conversation
1 similar comment
2ae5a23
to
cad4cb4
Compare
@ndelvalle since i was already looking at the code for our "programatic usage discussion" (220), i've gave it a try to fix the "iframe click issue", as i might have a situation on my app that might need it — a I've tried to squeeze the juice from the SO threads mentioned in #80. If you think this has "future" let me know and I will update the specs to keep code coverage on the green. ☮️ |
@renatodeleao thanks for the contribution! To be honest the fix seems a bit hacky but I don't see a pretty way to work around this issue, so LGTM 👍 |
@ndelvalle thanks for looking at it! I did a couple more searches but didn't find any cleaner contenders for this. |
1b9bd2d
to
4fdcb28
Compare
Clicks on iframe don't bubble, so they are undetected by our `document.documentElement` listeners. Workarounds in the SO threads and dev internet fall into 3 categories: - add pseudo-elements cover ups on iframes; - wrap iframes on an extra <div> and capture the click on it; - window.onblur + document.activeElement combo; The last one seemed the less intrusive: - it doesn't block clicks on iframe content itself; - it doesn't add extra DIVs which could cause layout issues; - it keeps performance impact to a minimum; How it works: The gist is that a click on an iframe will move `focus` to it. Since iframe has its own `window` our main app window will blur: on that event we can then check if document.activeElement is an IFRAME and execute the handler considering it a "faux click outside". Implementation details: - adds a custom `vco:faux-iframe-click` eventName id to the provided eventsNames array, this will be used to perform conditional logic: - bind `blur` event to window instead of document.documentElement; - use `onFauxIframeClick` instead of default `onEvent` as handler wrapper; - `onFauxIframeClick` doens't require the event path logic checks as it only uses `document.activeElement` and simply wraps handler execution into a setTimeout to workaround an issue with Firefox, that only sets `activeElement` correctly after blur, on the next tick (event loop). Caveats: - Click outside will be triggered once on iframe. Subsequent clicks will not execute the handler untill focus has been moved back to main window. This is the "expected" behaviour as by clicking the iframe, focus will move to iframe contents — a different window. There might be way to workaround this, by triggering `window.focus()` at the end of the provided handler but that will break normal tab/focus flow, therefore not included by default. - Moving focus to iframe via tab navigation also triggers `window.blur` consequently the click-outside handler.
some form of empiric evidence of feature working
4fdcb28
to
272e66c
Compare
update 📣 Since this isn't "a real fix" and having in mind the function processDirectiveArguments(bindingValue) {
return {
...
isActive: !(bindingValue.isActive === false),
useIframeFauxClick: !!(bindingValue.useIframeFauxClick === true)
}
}
function bind(el, { value }) {
...
el[HANDLERS_PROPERTY] = ['vco:faux-iframe-click', ...events].map(
(eventName) => {
const isForIframe = i === 0 && useIframeFauxClick
return {
event: isForIframe ? 'blur' : eventName,
... (e2e2bd1) Also feedback on naming is welcomed 😅 . |
@renatodeleao I really like the idea of having it under a flag!, WDYT of just a |
src/v-click-outside.js
Outdated
})) | ||
// Note: keep events array immutable, since events value defaults to | ||
// EVENTS variable reference. | ||
el[HANDLERS_PROPERTY] = ['vco:faux-iframe-click', ...events].map( |
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 handle the iframe case separately? If it has the iframe flag, attach only our custom event listener and discard all events and early return.
If it does not have the iframe case, use the standard code? WDYT?
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.
🤔 Not sure if I got it correctly, as in, even if we got the iframe
flag to true we still need to add the other listeners on bind()
to keep regular behaviour (clicks outside that are not on an iframe?).
7ecd9b4
to
64882b9
Compare
Because of the caveats now documented in readme and edge cases that we might not be aware of related. Implementation details: - Adds `detectIframe` config option - Move the whole fauxIframeClick event object under a separated if clause and merge it with the regular eventsObject array if detectIframe is true - Documented new config flag and caveats on readme - Updated specs accordingly
64882b9
to
a7ff8d1
Compare
@ndelvalle I've simplified (i guess 😅 ) the code and avoided so many ternaries — 5530376
|
@renatodeleao thanks for the great work! 🙌🏻🙌🏻🙌🏻 |
@renatodeleao v-click-outside 3.1.0 published 🎉 |
🎊 |
Attempt to close #80 with iframe click detection workaround.
Clicks on iframe don't bubble, so they are undetected by our
document.documentElement
listeners. Workarounds in the SO threads and dev internet fall into 3 categories:<div>
and capture the click on it;window.onblur
+document.activeElement
combo;The last one seemed the less intrusive:
How it works
The gist is that a click on an iframe will move
focus
to it. Since iframe has its ownwindow
our main app window willblur
: on that event we can then check ifdocument.activeElement
is an IFRAME and execute the handler.Implementation details
adds a customvco:faux-iframe-click
eventName id to the provided eventsNames array, this will be used to perform conditional logic:- bindblur
event to window instead of document.documentElement;- useonFauxIframeClick
instead of defaultonEvent
as handler wrapper;detectIframe
config flag that defaults totrue
— see fix: iframe click detection #222 (comment)true
, the following event object will be merged together withel[HANDLERS_PROPERTY]
array.el[HANDLERS_PROPERTY]
for event binding will occur, but now also binds our special blur event to thewindow
onFauxIframeClick
doesn't require the event path of regularonEvent
, i simply checks ifdocument.activeElement
is an iframe and wrapshandler
execution into asetTimeout
to workaround an issue with Firefox, that only setsactiveElement
correctly after blur, on the next tick (event loop).#caveats
section underREADME.md
Caveats
iframe
. This is the "expected" behaviour as by clicking the iframe, focus will move to iframe contents — a different window, so subsequent clicks are inside it. There might be way to workaround this such as callingwindow.focus()
at the end of the provided handler but that will break normal tab/focus flow, therefore decided to not be included in the source code.window.blur
consequently thefaux-click-outside
handler - no workaround found ATM.