Prevent default Press but not default Drag in iOS

2020-02-21 09:25发布

问题:

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?

回答1:

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.



回答2:

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.