I'm attempting to convert my web app into a form usable by mobile devices. I'm attempting to build in support for touch gestures like horizontal scrolling. I'm finding some strange behavior in my app.
I start a gesture with a touchstart event, and then scroll on touchmove. However, my application sees a 500-700 ms delay between receiving these two events. As far as I can tell, my app is doing no other work between these two events.
Other aspects:
The code is written in jquery, using
$(element).bind(touchmove, function(ev) {return myobject.DoTouch(ev) }
were the DoTouch command simply checks the ev.type, records the touch position, and returns false.
Any ideas what I should look for to try to solve this? The lag between touching and getting a response from the app is very annoying.
Yes. It turns out, this is how iOS works. I was pulling my own hair out for some time. Read more here: http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html. Essentially, if iOS thinks it can handle this as an internal PAN gesture, it does and doesn't even bother sending a touchmove event at all.
In my project, I found that if the viewer makes the touchmove gesture very deliberately and pauses a bit longer before lifting the finger at the end of the move, then the touchmove event is, in fact, sent as one might expect. So, the documented behaviour may be a little iffy versus reality, which only added to confusion and my debugging efforts.
Anyway, if iOS handles the event internally as a PAN gesture, it will send a scroll event before the touchend. In my project I was able to use this to set the flag I was using to distinguish dragging gestures (which was normally sent in my touchmove handler) and ignore any behaviour in stand-alone touchend handlers that were not related to the handling of my own scroll-handling.
I hope this helps you (and others) as well!
Related
I am attempting to use the data received from DeviceOrientationEvents to animate (rotate) a camera in three.js using three's DeviceOrientationControls. The controls are updated upon every animation frame, and everything works as I would expect. However, if I begin to scroll, then no DeviceOrientationEvent is fired again until the inertia from the scrolling is complete.
I have confirmed that these events are not fired (or at least not dispatched) during the scroll by logging to the console from within the DeviceOrientationEvent handler. I can see the events fired regularly up until the moment I begin to scroll, then stop, and then resume firing from the moment the inertia from the scroll is complete.
Manually stopping the inertia mid-scroll (by touching the screen) also causes the deviceorientation events to resume.
I have disabled all other scroll event handlers in my script. I have made all touch event handlers passive, have tried making them non-passive as well, and have also tried disabling all touch event handlers in my script altogether.
I am fairly sure by this point that this may be a function of how the processing of the scroll thread (which operates separately to the main thread) and the processing of IMU data are scheduled/queued in the browser, so that there may be no good solution, but I'm asking here in case there is something I've overlooked in my own troubleshooting. This does not appear to be an issue with three.js or the DeviceOrientationControls in three.js, but I've tagged this as three.js just in case anybody has ever come across this problem when attempting something similar.
My unique case for having DeviceOrientationControls enabled while scrolling is that scroll drives the animation of a "camera rig" (of empty objects whose rotation and position are animated), while moving the phone around rotates the camera itself. (It's a bit like being able to turn your head to look around while moving in a railcar.)
My testing has been on an IPhone 11 Pro, with iOS 13.5.1, in Chrome iOS 84, and Safari. I have not tested on Android.
iOS has slowed down repetitive JavaScript functionality during scroll for many years now. This is to conserve battery consumption, since it has to re-render the page lots of times while scrolling, so it halts other secondary commands until scrolling is complete. See here for more.
You could create your own custom scrolling functionality without actually scrolling down an HTML page by capturing vertical swipe gestures via 'touchstart' and 'touchmove'. Or you could use a library like Hammer.js to help you.
Making a nice quick-responding website is relatively difficult because of the conflicts between touchstart, tap and the 300ms delayed click.
Ofcourse vclick should fix these issues, but also they seem to have problems fixing it completely. From the documentation:
Warning: Use vclick with caution
Use vclick with caution on touch devices. Webkit based browsers
synthesize mousedown, mouseup, and click events roughly 300ms after
the touchend event is dispatched. The target of the synthesized mouse
events are calculated at the time they are dispatched and are based on
the location of the touch events and, in some cases, the
implementation specific heuristics which leads to different target
calculations on different devices and even different OS versions for
the same device. This means the target element within the original
touch events could be different from the target element within the
synthesized mouse events.
We recommend using click instead of vclick anytime the action being
triggered has the possibility of changing the content underneath the
point that was touched on screen. This includes page transitions and
other behaviors such as collapse/expand that could result in the
screen shifting or content being completely replaced.
Now I'm thinking about doing something simpler. Whenever a touchstart event is being triggered I know this is a touch device for sure. I just disable all click events, and start listening to touchstart (or tap) events only. Ignoring the 300ms delayed click events.
Of course there are devices with a mouse and touch, but people using these at the same time seem like a minority to me.
Is this a good idea, or am I missing something in my thinking?
First of… what makes you say that people that use both touch and mouse input are a minority?
The 300ms click delay has been gone a while now on Android when using <meta name="viewport" content="width=device-width">. Unfortunately it can't be removed on iOS because it's a scroll gesture on unzoomable pages that almost nobody seems to be aware of.
I'd say that the best approach is still to support both mouse as well as touch input, despite the 300ms delay on iOS devices. It's dangerous to assume a user will exclusively use touch input when they use it once.
Imagine a user happily using a mouse to navigate. They see something interesting that they want to look at a bit closer so they use a touch gesture to zoom in and all of a sudden mouse clicks don't work anymore. That sounds broken to me.
I just remembered an interesting discussion about detecting a mouse user. Maybe it'll help you see things a bit differently.
Yes, in my honest opinion it is smart way to go. This has proven to be quite a hard problem and when you still combine it to the compatibility problems caused by some really crappy mobile devices which don't follow standards even that bit, it quickly becomes a battle that you can't win. We have adopted a solution close to this, with realization of fact that there might arise problems with poor devices. But after all, you can't satisfy the needs of everybody and the distribution of usage tends to favor those devices (nowadays) that follow the standards.
Also note that you don't need to wait for first touchstart to happen. Instead you can do this trick after DOM is ready and bind the events accordingly.
var isTouchDevice = 'ontouchstart' in document.documentElement;
which is copied from KevBurnsJr's answer.
Also as you most probably already know, you can bind to all kind of events and then check when entering the callback of which type the event actually is with
event.type
Good luck!
It would be a bad idea to disable all click events on the basis of a single touchstart as you suggest. While using both pointers or touch at the same time isn't a common use case. Preventing dual use of mouse/pens and touch isn't a forward compatible approach.
And when you say: "Ignoring the 300ms delayed click events."
I think you make a false assumption on click. You'd still have to synthesize clicks one way or another. touchstart alone isn't a click action. An assumed click happens on touchend, not touchstart. Here is the principle behind detecting clicks early on mobile: https://developers.google.com/mobile/articles/fast_buttons
If you are looking for fastclicks you may want to look into the fastclick script or other fastclick ones on github instead of vclicks.
To avoid issues with people using both touchscreen and mouse with pleasant reactivity I suggest this in JQuery, it works good enough for me:
$elem.on('click touchstart', function(e){
var $self = $(this);
if(e.type == 'touchstart'){
$self.mouseenter(); //fire events you still need
e.preventDefault();
}
/* your code */
});
From my experience it's better than keep the delay on click event and some hazardous comportment through devices, but there's inconvenient too.
On the iPad I tested, it avoids the situation where hover event is triggered on first tap then click event on second tap, but also it seems that the click event fire when you tap near the border of your element and not the touchstart, have to keep it in mind.
Also, it seems not working well with 'tap' event, certainly because it's not well treated yet by JQuery.
First, excuse me please, this is not a programming question, but I think it wont fit to Theoretical CS, and CS Non Programming is still in private Beta.
after updating my phonegap Applications to listen to
$(selector).bind("touchstart",function());
instead of
$(selector).click(function());
(here with jquery),and the performance improved remarkably, I want to know what touchstart does different, despite of the fact that it is probably designed especially for mobile devices environments. I looked up the w3c document on touchstart, but it doesnt provide any information.
if anyone has a link to further explanation or can explain how it works, I greatly appreciate it
On the iPhone the touchstart event fires as soon as your finger touches the screen, whereas the click event fires 300 ms after you touch the screen and lift you finger off the screen. The 300 ms time delay is so that Safari can wait to see if you intend to double tap the screen in succession to simulate the zoom gesture.
I'm developing a web game application with a, HTML5 canvas that has to react to "click" events (in the general meaning).
On a mobile platform (or touch-capable), that means reacting to onTouchDown, on a desktop/laptop platform (with a mouse or pad), that means reacting to onMouseDown.
Problem is, if I handle both events, then sometimes the same "click" will result in both events getting fired, so I get a double signaling.
What would be the best way to handle that?
Currently, upon the first touch event I receive, I turn off mouse events, but that may be a bit heavy handed if the platform supports both touch & mouse clicks (f.i. an Android tablet with attached keyboard/pad/mouse)
Measuring the delay between touch & click to ignore a click after a touch doesn't work too well, as there are circumstances where user may touch/click at a high frequency, so a too long delay leads to dropped double taps/clicks, and a too short delay lets through the occasional double signaling slipping through.
I've looked at user-agent detection, but that seems quite fragile (many user agents out there), and doesn't solve the cases where the platform has both touch & mouse/pad.
Maybe a combination of your suggestion:
Measuring the delay between touch & click to ignore a click after a touch doesn't work too well, as there are circumstances where user may touch/click at a high frequency, so a too long delay leads to dropped double taps/clicks, and a too short delay lets through the occasional double signaling slipping through
And detecting the x,y coordinates of the tap would decrease the false positives. So if both events (tap & click) happening in sequence got the same coordinates they are handled as the same.
Or maybe let the user switch (through some sort of options screen) if he wants to use mouse or touch for his device if both is supported. Display a warning message for example saying you're on a touch device so we enabled touch events, if you're using a mouse please see options or something like that.
Most users would be happy with the auto-choice and everybody unhappy can change it.
A script I'm working on samples x/y mouse coordinates to determine a set of user gestures. It's currently attaching a mousemove event listener to document.body.
New requirements include adding listeners for mousedowns and keypress frequency. Those are simple enough. The other new requirement is replicating the interaction listening on mobiles/tablets.
My tentative plan is to avoid device-specific code like the touches object by using the legacy mouse and keypress support.
What challenges should I be aware of? For example, gotchas where a gesture with a mouse and a gesture with one finger will produce vastly different sets of x/y coordinates on mousemove... or a widely used mobile browser with super-odd event handling.
http://www.quirksmode.org/mobile/tableTouch.html is a wonderful reference.
Thanks!
Have you given jQuery Mobile a try? It comes loaded with all the touch gestures you'll ever need. I have used it and can confirm that it works across iOS and Android. Didn't quite test on other platforms though.
There is also this great jQuery plugin called jGestures which gives you access to many events such as touch, tap, pinch and even has a orientation change event.
jGestures: http://jgestures.codeplex.com/
Also I found this interesting page about touch events in Android and iOS and has a list of supported events on each platform: http://backtothecode.blogspot.com/2009/10/javascript-touch-and-gesture-events.html
Forgive me if I've misunderstood your question and posted something unrelated. Hope this helps.