Click Delay On IPhone and Suppressing Input Focus - javascript

The webkit browser on iphone has a 300ms delay between when a user does a touch and when the javascript gets a click event. This happens because the browser needs to check if a user has done a double tap. My app doesn't allow zooming so a double tap is useless for me. A number of people have proposed solutions to this problem and they usually involve handling the 'click' on the touch end event and then ignoring the click generated by the browser. However, it doesn't seem to be possible to suppress a click that gets sent to an input element. This can cause a problem if you have a dialog that opens above a form then a user hits the close button and their click gets routed to an input element when the form disappears.
Example with jqtouch (for iphone only)

You have to capture your event on touchstart if you want to get the fastest possible responsiveness. Otherwise you'll be doomed with this input lag.
You have to remember though that capturing event on touchstart and responding to it makes it impossible to cancel action by dragging your finger out of responsive area.
I have personally used this in my PhoneGap html/js based iphone application and it worked perfect. The only solution to give this almost-native feel.
Now regarding your problem - have you tried to stop the propagation of the event? It should solve your problem.
$('.button').bind('touchstart', function(e){
e.stopPropagation();
// do something...
});
hope it helps,
Tom

My colleagues and I developed an open source library called FastClick for getting rid of the click delay in Mobile Safari. It converts touches to clicks and handles those special cases for input and select elements cleanly.
It's as easy as instantiating it on the body like so: new FastClick(document.body), then listening for click events as usual.

I made Matt's FastClick a jquery plugin:
stackoverflow link
Just had a comment about the onClick handler being called without the necessary scope being passed. I updated the code to make it work.
There also seems to be a problem when input elements lie under the ghost event's position: the focus event gets triggered without being busted.

I see two problems in the question. One is handling the delay in click and the other is handling input focus.
Yes, both of these have to be handled properly in mobile web.
The delay in click has deeper reasons. The reason for this 300ms delay is explained very well in this article.
Responsiveness of the click event.
Thankfully this problem is well known and solved by many libraries.
JQTouch, JQuery Mobile,
JQuery.tappable,
Mootools-mobile,
and tappable
Most of these libraries create a new event called tap. you can use the tap event similar to the click event. This jquery-mobile event handling might help.
$("#tappableElement").tap(function(){
// provide your implementation here. this is executed immediately without the 300ms delay.
});
Now, the second problem is with the handling of input focus.
There is a noticeable delay here also.
This can be solved by forcing focus on the element immediately for one of the touchstart or touchend events. This JQuery event handling might help.
$('#focusElement').bind('touchstart', function(e){
$(this).focus();
});
$('#focusElement').focus(function(e){
// do your work here.
});
You can do e.stopPropagation in 'touchstart' event handling to avoid propagation. But I would strongly advise against return false; or e.preventDefault as that would stop default functionality like copy/paste, selecting text etc.

Related

.focus() doesn't work on chrome mobile when not activated with click. Workaround?

I've come across some strange functionality on chrome, mobile.
When I try to focus on an element on chrome, it doesn't work when you try to just load the input, getItById and do .focus(). However, if you wrap it in an event listener attached to a button, and click the button with your mouse, it works fine.
So, I tried to trick it by seeing if you could call btn.click(), but that doesn't activate the .focus()
Have a go below: On mobile, chrome (at least for iOS), load the page. You should get an alert 'Clicked', but it won't focus on the input. Then, try clicking on the button. You will get both the alert AND the focus works.
I found this interesting and wanted to see if people knew of a workaround.
Link here - jsfiddle
const btn = document.getElementById('button')
btn.addEventListener('click', () => {
alert('clicked')
const input = document.getElementById('input')
input.focus()
})
btn.click()
<input id="input" type="text">
<button id="button">Button</button>
Edit
Another thing I've noticed, is that if you put your phone go to sleep, and open it again, the focus() works without the click.
Edit 2 - added link for mobile
Why does this happen?
Because mobile devices usually need to change the screen dimensions when a keyboard is shown, they come with some restrictions on when a focus event can be triggered. This is intentionally limited browser behavior on touch devices to guarantee a good UX and avoid performance issues of resizing the browser window ( = potentially very expensive style recalculation).
There's some cross browser differences in how far these restrictions go, and some browsers have bugs on top of that.
But in general they all require an actual user interaction to have happened not too long before the focus is programmatically triggered. Using btn.click() is not the same as an actual touch event, and so the browser will ignore it seeing there was no recent touch event.
On a tracking issue on this behavior for Webkit, Apple provides a motivation:
We (Apple) like the current behavior and do not want programmatic focus to bring up the keyboard when you do not have a hardware keyboard attached and the programmatic focus was not invoked in response to a user gesture. Why you may ask...because auto bringing up the software keyboard can be seen as annoying and a distraction to a user (not for your customers, but for everyone not using your app) given that:
We bring up the keyboard, which takes up valuable real estate on screen.
When we intent to bring up the software keyboard we zoom and scroll the page to give a pleasing input experience (or at least we hope it is pleasing; file bugs if not).
This similar issue with the autoplay attribute, which also requires a gesture (or page load) to have happened recently enough, could be helpful in understanding the kind of problem.
How to work around these limitations?
Most of the time
Likely you don't need to, as most code that would trigger focus is running as a response to a user action and will be allowed to trigger focus. I didn't find data yet on how big the window of time is, but I guess it's big enough that in regular cases it's not a problem (unless you set timeouts of course).
A possible problem for those cases is the script would run so long that the event is already considered too long ago. In fact that's what happened in the related autoplay issue, where the code would request some network resource, and only after the response would trigger the video to play. Depending on the speed of the network/device the video would sometimes auto play, and sometimes not.
Technically the same could happen with an input that is only focused after a network delay making it not show the keyboard. As long as your code doesn't do any network requests you won't have this kind of problem.
On page load
This is definitely not a "fix" for the problem, but you can do a best effort to manage focus on page load by using the attribute specifically made for it. This still won't make a keyboard appear when it otherwise wouldn't.
The autofocus attribute is at least partially supported on all browsers.
Perhaps just calling focus() directly in a script on page load works, but again there's a chance that this code runs too long after the page started being displayed, especially on slower devices. This probably also happens when adding HTML with the autofocus attribute programmatically (e.g. React). If the initial HTML contains the autofocus attribute on the right element that shouldn't occur.
There is deinfitely something weird happening here.
It seems "part" of the DOM thinks that the input element is in focus initially, but chrome does not complete the focusing. For exmaple, if the background is set with:
document.activeElement.style.backgroundColor = "pink";
Then the input element's background is pink. So, some part of chrome's DOM thinks the input element is in focus.
Initially, in my case, the input element is rendered by chrome in its default non-focus styling.
The alert() seems to be interfering with the process. In my case, taking the alert out had the effect of changing the styling of the input element from non-focus styling to in-focus styling, but the cursor did not appear in the input element, nor did the pop up keyboard appear.
By:
removing the alert
wrapping the click inside a function
calling that function on load
fixed the problem in my case. Initially now the element is in-focus styled, the cursor appears in the element's box, and the keyboard pops up.
The solution to the posting by #HJo, January 2019, is to replace:
btn.click();
with:
function load() { btn.click(); }
remove the alert(),
and make the body tag as:
<body onload="load()">
#Peter. If this does not fix your problem too, can you please provide minimal code for your context.
const event = new Event('tap');
const btn = document.getElementById('button')
const input = document.getElementById('input')
btn.addEventListener('click', () => {
prompt('clicked')
input.focus();
})
window.onload = function(){
btn.dispatchEvent(event);
}
}
The code above works when the website tab is just opened, but not on reload for some reason. I tested it on an Ipad and Iphone so hopefully it works well on your end too.
Here's a link to a repl:
https://r.hackinggo306.repl.co
Edit: This seems to be the closest I can get, but also acknowledge the fact that mobile devices might be deliberately preventing this. (It would be annoying if a website looped this.)
On the mobile, you will need to use touchstart event instead of click event.
You can find it here. Hope this helps.
I think, this problem can occur, because of the your JavaScript script is executed before the DOM is Mounted.
so you can do different things,
One Thing
add defer keyword to script tag. Basically it does is, that script is only run after the DOM is mounted. See
Second Thing
Wrap window.onload event with your input focus functionality.
<script type="text/javascript">
window.onload = function () {
const btn = document.getElementById('button');
const input = document.getElementById('input')
input.focus()
}
</script>
window.onload event is only fired when the DOM is finishing mounting.so that may be your JavaScript script run before the input element is mounting.so add onload event to your code.see this

JQuery click event triggered on Chrome and FF device touch emulation?

In my Rails site, I have an element that I want to act as a link when clicked by a mouse (e.g. on a desktop), but not when touched on a touch-screen device (because there is also a hover behavior).
My understanding is that the JQuery .click event should not get triggered on a touch.
My coffeescript for setting the click handler is simply
...
$(this).click ->
location.href = url
...
(where "this" is the element in question)
I know this code works, because the click action works with the mouse. To ensure that it doesn't get triggered on a touch device, I use the device emulation in Chrome's Developer Tools to emulate a touch. However, when I do this, the method still fires and follows the link.
Is this a problem with the Chrome emulation or with my code? I want to know whether it will behave this way on real touch devices.
edit: Same thing happens with Firefox, so I'm thinking it's my code...
I realized that touch events trigger click events as well, later on in the event chain. To get the functionality I wanted, I kept the .click handler, but added a .touchstart handler where I called event.preventDefault() to short-circuit the rest of the event chain. This way, the .click handler fires for mouse clicks, but not for touches.

JavaScript: Swipe for Action Pattern Implementation

I have implemented the Swipe for Action Android pattern in my mobile web application (PhoneGap) using JavaScript & CSS animations/transitions.
However, there's one thing that's still eluding me.
I wish, that once the action menu is displayed fully and the user clicks anywhere outside of the action menu (labelled 3 in the figure), the menu should retract and the original item displayed (labelled 1 in the figure).
In a desktop application, one could "capture focus" and perform the transition back to (1) in lostfocus.
What is the JS equivalent of lostfocus event. I see an onfocus and onblur event, but from what I read it's really meant for things that need focus; like input, textarea, etc.
How else could I catch that event I'm interested in, other than putting some code in the touchend of every other element in the page and forcing the retraction of open actions explicitly?
I think you gave the answer yourself. focus and blur are the events to be used for this and they are not exclusively meant for input elements, as you can see here [1].
I'm even trigger the focus event manually in a layer use case: A layer opens and I want to capture the keypress of ESC to close the layer. For this I need to set the focus on the layer as my event handler would not fire otherwise.
To capture the click outside you just need to register for pointerUp or click events on an element that spans the whole screen (it must really cover the whole screen like the body element). Because of the event bubbling the handler will fire as long as nothing else captured and cancelled it.
[1] https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-blur

Jquery mobile page seen much after the pageshow event on an android phone

I am building a cordova/phonegap app using Jquery Mobile. My app feels very sluggish right now and I see that the main reason is that the page only shows up after about a second after I see the pageshow event being fired. Ideally, I expected it to be shown when the pageshow event is fired.
Also, during this meantime(after the pageshow before the page is actually shown) if I touch on the page at a certain point it fires the ontouch event on the item that is supposed to be present at that point. So the page is already there but maybe it's taking this time to render.
Do you know how to make this page render faster using JQuery Mobile? Is there something I can do with the custom Jquery Mobile builder that helps Jquery Mobile not do stuff that's not required?
Please note that I have turned off transitions globally on my app using
$.mobile.defaultPageTransition = 'none';
The mobile browsers has a delay of 300ms delay for touch events. To disable this you can use fastclick. It can remove that 300ms delay in your app.
https://github.com/ftlabs/fastclick
This can help you too to make it work faster.
The browsers wait for 300ms to check if the user has done a single click or is going to do a double tab. If the user has not touched again before 300ms, its considered as a single touch click.. else it's considered as double tab.
Just posting another answer as I think this would be the solution for your updated question.
If my understanding of your problem is correct, disabling the DOM cache will solve it.
$(document).bind("mobileinit", function(){
$.mobile.page.prototype.options.domCache = false;
});

How to properly handle left and right click in Firefox

I am working on a web app in which I want to have a different action happen to an element whether I left or right click on it.
So I first added a function to handle the click event with jQuery, and then added a second function to handle the oncontextmenu attribute of my element.
This is working well in Chrome & IE but causes a problem in Firefox: When I right click on an element, my function that handles the left click is surprisingly called, and then my function that handles the right click is called.
How can I make Firefox not call the left-click function when I right click?
Yeah, browsers traditionally send right-clicks to the onclick handler, with the event.which property set to 3 instead of 1. IE used oncontextmenu instead, then Firefox picked up oncontextmenu in addition to the usual onclick. To cater for the browsers you will have to catch both events — or find a plugin that will do it for you.
Note that even with this sorted out, you are still not guaranteed to get right click events or be able to disable the standard context menu. Because many web pages abused the ability, it is disablable in many browsers, and sometimes disabled by default (eg. in Opera). If your app provides right-click actions, always ensure there is an alternative way to bring them up.
My problem came from the fact that on one side I was using the insanely great jQuery live function for click and the oncontextmenu attribute on the other. (Using onclick and oncontextmenu was not a problem).
I've just modified my $.live("click"...) function by catching the event and not firing the rest when e.which is 3.
Problem solved!

Categories