Prevent default Press but not default Drag in iOS MobileSafari? - ipad

Context: Web app on MobileSafari/iPad, both full-screen and embedded in iframe
Goal: Technique that provides custom event handlers for Press and Tap but not Pinch (zoom) or Drag (scroll/pan). We want to provide custom behavior for Press, but to let Safari handle Scroll/Pan/Zoom still.
Problem: The two goals seem to be mutually exclusive. To prevent the default behavior for a Press gesture, event.preventDefault must be called in immediate response to ontouchstart. However, to let the default behavior proceed on a Drag, we must not call event.preventDefault on the initial ontouchstart. Once preventDefault is called, there is no way of getting the default behavior back during that gesture sequence (i.e. until all fingers come off). Drag is not recognized until some movement has occurred.
Drag is illustrated as the simplest example, but we care about getting the default behavior for Pinch and Double-tap as well. We never want the default MobileSafari Press behavior of "Copy Image" or selecting text.
Attempts thus far:
Save the ontouchstart event and, after a timeout, call preventDefault on it later (just before Press would be recognized). No effect.
Listen for 'oncontextmenu'. Nope, the default Press behavior is not being signaled or routed through this event.
Prevent the default in all of our ontouch* handlers, and then when we recognize a Drag/Pinch gesture, simulate new events using initTouchEvent and/or initGestureEvent. The programmatically created events hit all of our callbacks, but Safari seems to pay no attention to them, triggering no default behavior.
Break up Safari's Press recognition with a fake move. Safari cancels a press on the slightest move, but we allow for a bit of slop. Simulating a move event (as above) just after the ontouchstart does not cause Safari to fail to recognize Press if the finger is not moved "for real".
We have a fully featured set of gesture recognizers implemented (in Javascript, ontouch*) in the style of Apple's native iOS gesture recognizers. We have no problem recognizing any gestures, we just don't know of a good way to replicate the Zoom/Pan/Double-tap behavior that Safari provides for free.
We don't need code; we're looking for any theoretical solution (beyond "just implement pan/zoom yourself too", though if you have a slick way of doing that we're interested) that leads to reasonable success. We're also interested in learning about other similar attempts to do this - surely we can't have been the first to try this?
Alternate TL;DR: Is there any way to prevent a default Press (also known as tap-and-hold) other than in touchstart?

Success: Prevent "default press" behavior entirely through CSS instead of preventDefault. This answer was provided by Safari Technologies Evangelist Vicki Murley over here (registration required)
I see in your stackoverflow post that
you say your ultimate goal is that:
"We never want the default
MobileSafari Press behavior of "Copy
Image" or selecting text."
These behaviors can be disabled in CSS
(+ one bonus property, since people
often want to turn off the highlight
if they're turning off these other
behaviors):
/* behavior */
-webkit-user-select: none; /* disable cut copy paste */
-webkit-touch-callout: none; /* disable callout, image save panel */
-webkit-tap-highlight-color: transparent; /* "turn off" link highlight */
Commentary: We assumed that Safari's Press behavior was event-based, not driven by CSS. Calling preventDefault ontouchstart does cancel the press behavior (which is what led us astray, really), but that seems to be an unintended side-effect. Really, Mobile Safari does not execute a "press event" so much as "CSS on press".
Disabling the Press behavior with CSS has allowed us to once again call preventDefault only where and when we actually need it.

Ugly Possibility: Don't prevent a press - make a default press do nothing. Try to use a glass-pane div that catches all touches and doesn't prevent anything on touchstart.
Divs do have default Press behavior (some kind of selection), but perhaps that can be turned off, not via preventDevault but via <body style="-webkit-user-select:none">? ref
This would mean we'd have to do our own hit-testing to determine what DOM nodes to pass our recognized events to, since we can't just let events bubble up the DOM ancestor chain.

Related

Button retains focus when mousedown and mouseout, accesibility implications

It seems to be a consistent browser behaviour that when the user drags out of a a element, to avoiding clicking on releasing the mouse button, that that element retains focus until the user clicks somewhere else.
Is there an accessibility related reason for this?
It seems to be a common user behaviour (interaction) to negate a click 'halfway through' and, in MacOS, at least, is the default menubar mouse behaviour.
I guess I'm looking for a way to force a drop of focus on mouseout but I don't know whether this is bad for accessibility.
Incidentally this is visually bad ugly either button is used as a UI object or an a is styled as a button.
Whether the focus remains on the button/link or if you force it away will not cause an accessibility issue with regards to WCAG conformance. It does not fail any guidelines.
It comes close to failing WCAG 3.2.2 On Focus but I think the timing of your events technically makes it pass. The mousedown event causes the focus event and you are not doing anything with the focus event. It's when the mouseup event happens that you're trying to move focus so in theory, you are not changing the context on the focus event.
I'm not sure that means you should do what you have proposed from a UX perspective but there probably aren't a lot of people that rely on the default behavior.

Disable touch scrolling in xterm.js

How can I disable scrolling by touch on xterm.js?
I have a touchmove event on term.element that simulates cursor keys by swiping the appropriate direction. This works great in applications such as Midnight Commander that use the "alternate screen buffer", but in the default screen buffer that has scrollback (i.e. lynx or bash, for example), it scrolls the terminal (as it normally would) in addition to sending the ansi codes for the arrow keys.
I need to override this behaviour so scrolling does not happen when you swipe, without preventing scrolling by other means such as the mouse-wheel or the scrollbar, or interfering with other mouse events (and possibly touch events).
Tried preventDefault() on the event, even tried attaching it to various elements within the terminal, parentNode, and its parentNode. Same behaviour. Also tried position: absolute with bottom: 0px, but the way xterm.js simulates a terminal makes for abberant results.
Any ideas?
Ok. The solution is: add event.stopPropagation() at the top of the handler for the touchmove event. Attach it to the terminal element (i.e. term.element). Forget the overlay, pointer-events: none, etc. Works perfect. No additional garbage. I left in event.preventDefault() for another reason, but I think just the stopPropagation stands alone for this particular question.
I would like to add, if you just want to disable touch scrolling on the terminal all you need is:
term.addEventListener('touchmove',function(e){e.stopPropagation()});
Assuming your terminal object's name is 'term'.

Tablets hover on first click, click on second click

Posting this question largely in the hopes of confirming my suspicions of the behaviour, and thus documenting it for other programmers. (Since I found no record of this anywhere online)
I have a site that I'm building, whose nav bar has the following properties:
The horizontal section is a <ul> of <li>s and some of the <li>s hav both:
A n<a> element taking you to that topic.
A hover CSS selector that triggers display:block on a submenu - a nested <ul> which then drops down vertically.
On a desktop this all behaves as I'd expect: hovering on the key element exposes the submenu, and clicking on it executes the click event (in this case a normal <a> link.
But on an iOS device (tested on Air, Mini, iPhone 6) I found that tapping once would expose the submenu, and tapping a second time (when the submenu is open) will actually invoke the link on the controlling element.
Long-pressing will bring up the "link context menu"
This is exactly what I wanted it to do, which is GREAT! But I don't know WHY it is doing it. Whilst the Menu is Bootstrap based, but I can't find any bootstrap that is doing it.
Currently my best guess is that iOS Safari has some magic code that adds this (obviously desirable) behvaiour by deciding that if you have an element with :hover CSS (or, I imagine, an onhover eventhandler bound) and also a click eventhandler bound then the first tap will invoke, and keep invoked, the hover event, and the second tap will invoke the click event.
Question:
Does anyone know, confidently, what the source of this behaviour is.
Can anyone find any documentation of this behaviour!?
Would people like to contribute other platforms on which this does/doesn't work (Android tablets? Windows tablets? older iOS?)
The behavior triggering a clickable element such as an anchor link to fire only on the second tap in iOS, is described in this post by Nicholas C. Zakas (#slicknet). What triggers the double tap is a:
:hover Rule that either hides or shows another element using
visibility or display.
e.g.
<style>
p span {
display: none;
}
p:hover span {
display: inline;
}
</style>
<p>Tap me<span>You tapped!</span></p>
Apple also provides a documentation on Handling Events for reference.
No other platforms do this. It's iOS specific since at least version 5 (likely since version 1). Because it's not cross-platform, for Android and other touch devices, it has to be handled differently, and requires canceling click events etc... using JS. While I have managed to do this. I think it's fair to say that for CSS navigation menu bars to work with toggle elements that are both links and toggles is very difficult to achieve in a touch-only environment.

In a UIWebView is it possible to determine when the user has clicked somewhere that isn't a link?

I'm displaying HTML content using a UIWebView and would like some action to occur whenever the user clicks anywhere that isn't a link.
I added a UITapGestureRecognizer, however at the point handleTapGesture is called I do not yet know if the user has clicked on a linked or non linked part of the screen.
To work around this within handleTapGesture I added performSelector:#selector(performAction) withObject:self afterDelay:n]
Then if shouldStartLoadWithRequest is subsequently called I set a flag that performAction checks to see if it actually should perform the action or not.
This works perfectly - but only if the time delay n is sufficiently long enough (about 0.3 to 0.4 seconds) which is too long from a users perspective as its a noticable delay. Also I guess the value of n would likely need to vary on different devices with different processor speeds and so its a fragile solution.
Is there an elegant solution?
Do you have control over the HTML? If so, you can add a global click detection, e.g. <body ontouchdown='detectTouch()'> and then use a custom message to your ObjC app, e.g. location.href="touch://whatever". Watch for the link/location change messages in your UIWebView (shouldStartLoadWithRequest) and parse the scheme and data passed. Don't propagate on link clicks. Should work good.
If you don't have control over the HTML, you can always inject your touch handler, but it's trickier as you'll need to figure out the order of events during a link click, and ignore the ones that aren't links.

How to tell what device triggered a particular event in Delphi?

I'm wondering if there's an easy way to tell which input device triggered a particular GUI event.
For example: A TButton.OnClick event gets fired. Did the user trigger it with a keyboard press (shortcut, Enter key for default button, space key for a focused button, etc.) or was it triggered with a mouse click? Is there any easy way to tell?
The reason I'd like to know is so that I can implement keyboard usage hints into some of our applications when the user uses the mouse to initiate actions that could also be done with the keyboard. Our systems on the shop floor are in pretty dusty/dirty environments, and mice tend to not hold up so well in them. Also, in many cases, there's simply not that much room for a mouse to be used. (No, keyboards without numeric keypads is not a solution. They're relied on too heavily.)
However, since our apps run in Windows, users tend to simply use the programs like they would at home -- with a mouse. There's nothing particularly wrong with that, but we've worked hard to optimize the input workflow to be keyboard friendly as well. It'd be nice if there was a low-impact way to indicate to our users that there's a way for them to do the things they're doing without having to grab the mouse.
There's no way to tell from within OnClick. However, you can also attach events to a control that will fire when the mouse rolls over it, which would probably be more appropriate for what you're trying to do anyway. Take a look at the OnMouseEnter and OnMouseLeave events. Also, if you really want something specific to happen when the mouse is clicked, you can attach it to OnMouseUp.

Resources