I've run into some event related issues with Google Chrome (78) on mobile. I have a setup where a file input (including the label) is appended when clicking a button. The file input will replace the button entirely and will be appended in exactly the same spot as where the button originally was.
On most browsers, this works fine. However, it seems that the mobile version of Google Chrome will fire a delayed click event after the initial touch on the button, causing the click to activate the file upload dialog. This click is fired on the file input, even though the file input was not even present during the initial touch.
It's not possible to stop the propagation on the button click, since I am listening for a TouchEvent, but the additional event is a MouseEvent. I'd rather not listen to the MouseEvent just to stopPropagation to prevent any cross browser issues. PreventDefault is also not an option, since the button is located in a slider, which would break the slider functionality.
Another option is to hide the file input until an event loop has passed (setTimeout of 0), which essentially fixes the issue, but I'm hoping for a more elegant solution. Do any of you have any potential fixes for the issues?
I've set up a CodePen with a minimal code example showcasing the issue: https://codepen.io/frankderouge/pen/wvvRPRV
The basic set up is that a file input is initially hidden and then shown when a button is clicked.
//This listener is added to the 'button'
document.querySelector('.toggle_off').addEventListener('touchstart', (e) => {
//This won't do anything since we're handling a touch event, not a click event.
e.stopPropagation();
//This fixes the dialog open but would break sliding functionality
//e.preventDefault();
//Hide the button
e.target.style.display = 'none';
//Then show the file input, on which the additional event will be triggered
document.querySelector('.toggle_on').style.display = 'block';
});
Thanks in advance!
It turns out this is known as a 'ghost click', which exists for compatibility reasons and is actually 'expected behavior'. I did not expect a ghost click to occur if the mouse event was not bound to the original element in the first place, but apparently this does happen in some browsers.
The ghost click can be prevented by calling preventDefaulton a TouchEvent, in the initial post I stated that:
PreventDefault is also not an option, since the button is located in a
slider, which would break the slider functionality.
But I've recently learned that it does not matter on which 'part' of the touch the preventDefault is called on since the ghost click is always triggered after all the touch events. Therefore the issue with the scrolling being prevented does not occur if preventDefault is called on the touchend, rather than the touchstart or touchmove.
document.querySelector('.element').addEventListener('touchstart', (e) => {
//Process the touch start event
});
document.querySelector('.element').addEventListener('touchend', (e) => {
//Prevent default on the touchend, preventing the ghost click from happening
//But still allowing the users to zoom and scroll
e.preventDefault();
});
Related
I've a page containing CreateJS media, which gets embedded in another page via an <object> element. At some point in the timeline, I have my TweenJS code attach a keydown event listener to document which adds text to an object on the stage accordingly, since CreateJS doesn't offer support out of the box for keyboard events.
This works absolutely fine if I launch the media page on its own, but when I embed it, the keyboard events don't fire. I've tried adding a -1 or a 0 tabindex to the canvas element and calling focus(), which mostly works - until backspace is pressed, which causes IE to go back a page in the history. Calling KeyboardEvent.stopPropagation() doesn't seem to prevent this behavior.
I don't really have the option of using an <iframe> instead of <object> because the <object> is being generated by other code.
I have to support IE 11 and Edge, and it's not working right when embedded in either browser.
The problem ended up being that I didn't properly stop the propagation/bubbling of the event. I was calling just KeyboardEvent.stopPropagation() and nothing else. This snippet provided by a coworker fixed it:
function (event) {
if (event.stopPropagation) event.stopPropagation();
if (event.preventDefault) event.preventDefault();
event.cancelBubble = true;
event.returnValue = false;
// Event handler logic goes here...
}
This was overlooked because the behavior I was seeing suggested that the canvas never received the KeyboardEvent for the backspace key, and that IE was intercepting it (i.e., the browser went back a page without any changes to the canvas - I would have expected the key event to be handled and the canvas to render its changes before that happened).
I am running my web app on Chromium on a touch device. It's an industry control panel, not a consumer tablet.
I have a problem when the user means to click (tap) on a button, but their finger moves a little, thus causing the 'ontouchmove' event to fire. I other words, the device reads it as a swipe instead of a click.
When this happens, the click event is not fired, so nothing happens, even though the button's hover is triggered. So, the user sees that their touch was registered (button changes color), but is then frustrated by the fact that nothing happens.
I'm hoping I can put something in index.html that globally converts these swipes into standard old-fashioned clicks.
document.ontouchmove = function() {
// get target of this event
// fire the click event of that target
}
Is this even possible? I have jQuery installed. Is something like hammerjs needed? I'd like to get by with plain old JavaScript, if possible.
Thanks
I'm not really sure but you can try it :
document.on("touchmove", function(e) {
$(e.target).trigger("click");
});
I have a tablet html app. Some pages have <input> and <textarea> together with many other elements: links, menus, texts, ...
If I don't press any <input> or <textarea> everything works ok
As soon as I press one input element, the soft keyboard pops up (as expected).
After entering some text and hiding the keyboard, the keyboard pops up again everytime I click anywhere on the webapp (even in non-focusable elements)
This totally ruins the experience, as you are forced to use the web app with the keyboard always shown.
I have tried many different approaches to manipulate the input focus without any success, like calling blur(), focus() and related methods on the focused component, containers, window ... but seems nothing but reloading the page resets the keyboard state to keep hidden again until a focusable element is tapped.
My experiments:
Checked that pressing outside of the <INPUT> / <TEXTAREA> causes the focus to be removed: onblur() gets called, and document.activeElement returns NULL.
Also tried to manually blur() everything in the document after an onchange is triggered:$("input,textarea").blur() .
Tried to manually giving the focus() to a non-interactive element with a TABINDEX (hacky):
<div id="dummyfocus" tabindex="0">
$("#dummyfocus").focus()
I checked that the dummy element in fact receives focus, the input/textarea unfocuses, but even in this case, the problem persists.
In Android or IOS everything works as expected: Keyboard will not auto-show if no <input> or <textarea> is focused.
Any advice? Any funky microsoft-proprietary css tag I haven't heard about? :)
I have similar issue, after some digging find out that issue reproducible on Microsoft Edge browser( used in win10 uap as rendering engine).
When clicked anywhere active element becomes body element and for some reason (maybe bug) keyboard gets activated, so I added tabindex=0 on container div which is nested in body, so when clicked outside of any focus-able element that container becomes activated element and keyboard popup isn't fired.
for checking which element is activated I used this code
document.body.addEventListener('click', function() {
console.log(document.activeElement);
});
Hope this helps.
I managed to resolve this by adding the following if you are still interested. Added a dummy control to take the focus then change the focus when clicking away from the text area.
$("body").click(function () {
$("#radioDummy").focus();
});
$("#MyTextArea").click(function (e) {
e.stopPropagation();
});
It seems the issue lies in the touchstart event when using the keyboard on Windows 10. When you hide the keyboard (e.g. pressing the close button (x) on the keyboard), the input field still has focus. And when you press anywhere else, the keyboard pops up again. However, pressing anywhere for a longer time (long press) will remove the focus from the input, and not show the keyboard again. This got me thinking, that the problem could be solved hooking up the touchstart event, and prevent the event propagation, and remove the focus from the input.
I created a global #HostListener inside my main AppComponent that listens for touchstart events. When the body is clicked, stop the propagation of the event, and call document.activeElement.blur() (Loose focus).
#HostListener('document:touchstart', ['$event'])
globalTouchEvent(event) {
if (event) event.stopPropagation();
document.activeElement.blur();
}
I have created a Stackblitz that you can test using a Windows 10 tablet.
I'm currently working on an app that has some legacy Flash pages. We recently added a dropdown in the header that opens into a scrollable div. The header and dropdown are HTML.
Here's a wireframe to illustrate the set-up.
https://wireframe.cc/27ahkk
We ran into an issue where using the scroll wheel while hovering in the dropdown did not behave as desired.
Dropdown does not scroll
Flash content scrolls instead
I expected this was because, as part of Flash's processing of the wheel/mousewheel events, it was preventing bubbling and preventing the default action. I did some initial tests in Chrome and found that any event listeners I attached to the bubble phase were in fact not called.
My solution was to attach a wheel/mousewheel event listener to the window on the capture phase. In that handler, if the mouse is hovering over our dropdown, I call event.stopPropagation() to prevent the event from ever reaching Flash (thus allowing the default action to occur). Roughly:
window.addEventListener("wheel", function (event) {
if ($('.dropdown-selector:hover').length != 0) {
event.stopPropagation();
}
}, true);
This event is removed when the dropdown closes.
This worked fine in Chrome, and it works in IE as long as you fall back to the deprecated "mousewheel" event instead of using the more modern "wheel" event. Firefox, however, is a different story.
While Firefox supports the "wheel" event, it seems that the way in which events are sent to Flash from the browser is entirely different.
If you are hovering over the dropdown and scroll with the mouse wheel:
The handler will fire
The if condition properly detects the hover
event.stopPropagation() is called
However, Flash content still scrolls
Dropdown div does not scroll
Even stranger, if you are not hovering over the dropdown and you scroll with the mouse wheel:
The handler never fires
Flash content scrolls
This is different than the observed behavior in Chrome and IE, where scrolling while hovering over Flash will still fire the handler, but since the mouse is not over the dropdown, event.stopPropagation() is never called and thus it captures down to Flash where the event will be handled.
This is confusing to me because I attached the listener to the window in capture phase, so I should be receiving the event before anything else on the page. But in Firefox, it appears that either Flash gets the events even before window, or Flash receives a different event altogether (Flash appears to receive these events even if the JavaScript is stopped on a breakpoint in the debugger).
Does anyone have experience with Flash and Firefox and have a better understanding of how the browser sends events to embedded Flash content? Why does my strategy work in all the other browsers but fall short in Firefox? Any possible workarounds before we try to delve into the Flash code itself to work on a solution?
So I found a fix for this issue, although not entirely sure why it works. Even though Firefox supports both the "wheel" and "mousewheel" events, registering our listener to these events did not produce desired behavior. However, listening to the "DOMMouseScroll" event worked beautifully.
Not sure if this is due to some browser-specific logic and/or how our legacy Flash code handles scrolling, but it works so I'm satisifed. :)
I would like for a button to react to 'touches' on a touch screen, and 'clicks' on a non-touch screen. In case of a touch screen, I only want the touchstart handler to direct my flow and prevent the system from further handling the click event.
For this, I added two event listeners to my button. One listens for touchstart events. Once a touchstart has been detected, it uses event.preventDefault() to cancel the click event.
The code below on a touchscreen in Chrome, Opera and Android browsers achieves the desired result:
- one alert saying "touch".
However, in Firefox both events are detected, in spite of the preventDefault() in the touchstart handler:
two alerts... first one says "touch", followed by a second alert saying "click".
lginButton.addEventListener('touchstart', function(event) {
event.preventDefault();
alert("touch");
}, false);
lginButton.addEventListener('click', function(event) {
alert("click");
}, false);
Why is this happening and how can I achieve the desired results in all browsers?
I am currently unable to test. But based on your feedback it appears that Firefox is in fact not following the spec on events found here. I was about to find 4-5 bug logs on Mozilla that discussed this very topic, and finally found one that resolved the issue here https://bugzilla.mozilla.org/show_bug.cgi?id=977226. Definitely check to make sure you have the current version of firefox. And if you do it may be worth opening a new bug with mozilla on this. In the meantime you can set up a conditional inside of your touchstart event handler that detects the event type and handles touch and mouse events differently.. And then further adding more conditionals within you click conditional to handle browser and OS version. which is super bad practice, but would keep you going until a patch was implemented.