ios15 Safari- disable opening tab bar on double tap - javascript

How I can prevent opening of tab bar every time user double clicks on my web page on new ios 15 safari in portrait mode.

One way to prevent the floating address bar from popping up on tap or swipe is to use preventDefault() on touch / mouse events. I have found this is working for me on iOS 15 Beta 4 when dealing with a canvas taking up the full screen:
const preventDefault = (evt) => {
evt.preventDefault()
}
// Make sure to remove these event listeners later.
canvas.addEventListener('touchmove', preventDefault)
canvas.addEventListener('touchend', preventDefault)
canvas.addEventListener('touchstart', preventDefault)
canvas.addEventListener('mousedown', preventDefault)
If you don't have a canvas you'll instead want to attach it to a different full screen DOM element. In my testing I found some issues with using document or body, so you may need to do some work & testing to determine what works and is safe to use (i.e. doesn't prevent button's from working).

Related

Responding to touchstart and mousedown in desktop and mobile, without sniffing

So, I have a problem. I want to respond to a user pressing the mouse button (on desktop) or touching a div (on mobile). I'm trying to be compatile with evergreen browsers. This is what I tried so far:
listen only to mouseDown event. This works on desktop but doesn't work in mobile if the user is dragging. I want the handler to be called as soon as the user touches the screen, no matter if they're moving their finger in the process.
listen only to touchStart event. This works on mobile and desktop, except for Edge and Safari desktop, which don't support touch events.
listen to both, then preventDefault. This causes a double handler call on Chrome mobile. It seems that touch events are passive to allow uninterrupted scrolling on mobile Chrome, so preventDefualt has no effect on them . What I get is a warning message saying "[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080" in the console, preventDefault is ignored and my event is called twice.
Obviously this can be solved by sniffing touch events, but the net is full of self-righteous rants on how one has to be device-agnostic and that it's dangerous to detect touch events before the user interacted.
So I guess that the question is: is there a way to do what I want to do without sniffing for touch events?
Below my sample React code:
function handler(e) {
console.log('handler called')
e.preventDefault()
}
export default function MyElement() {
return (
<div
onMouseDown={handler}
onTouchStart={handler}
>
Hello
</div>
)
}
It turn out it's not yet possible in React. One workaround is set a flag the first time touchStart it's received.
touchHandler = () => {
this.useTouch = true
this.realHandler()
}
mouseHandler = () => {
if (this.useTouch) return
this.realHandler()
}
With the caveat that the first touchStart can be lost in case of dragging.
Quite disappointing.

Prevent force touch event on image but still allow long press event in iOS Safari

We have a need in our image gallery to prevent Apple's Force touch events on images, but still allow the long-press which triggers the 'Save Image' callout. We provide instructions for our iOS users to long-press on the image and then select 'Save Image', but users get very confused if they accidentally press too hard and trigger a Force Touch event - especially if it 'pops' and loads the image in a new page.
Initially I thought of listening to the touchforcechange events, and then calling preventDefault when the force reached a certain level. Something like this:
imgEl.addEventListener( 'touchforcechange', 'onTouchForceChange', false )
function onTouchForceChange( e ){
if( e.changedTouches[0].force > 0.5 ){
e.preventDefault()
}
}
However, that seems to block the long press events as well. There also doesn't seem to be one particular force level at which the event switches to a Force Touch.
Adding the css property -webkit-touch-callout: none; to the image does block the Force Touch event, but again, it also blocks the callout on the long press.
Any ideas greatly appreciated!
(Using jQuery, but can probably be accomplished without it)
this seems to be working for me, tested on iPhone 7 plus iOS 10.3.3:
window.addEventListener('touchforcechange', function(event) {
var force = event.changedTouches[0].force;
var id = event.changedTouches[0].target.id;
if ($('#' + id).hasClass('class') && force > 0.1) {
event.preventDefault();
console.log('prevented 3D touch on element with id = ' + id);
}
});
replace 'class' with the class of the elements on which 3d touch should be prevented. in your case, probably a class shared by all the image elements in the galary.
I think the main problem with yours is that 0.5 is probably too high of a threshold.
You can embed your image in an svg tag to prevent this.

Disable taphold default event, cross device

I'm struggling to disable default taphold browser event. Nothing that I have found on Google provided any help. I have only Android 4.4.4 mobile and Chrome dev tools for testing. I tried CSS fixes, such as webkit-touch-callout and others, but apparently they don't work for Android, also they don't work in Chrome dev tools.
I also tried detecting right click, (e.button==2), it doesn't work.
I came up with a solution, but it solves one problem and creates another. I just want to have a custom action for 'long press' event for selected anchors and I don't want the default pop up to appear (open in a new tab, copy link address, etc.)
This is what I did:
var timer;
var tap;
$("body").on("touchstart", my_selector, function(e) {
e.preventDefault();
timer = setTimeout(function() {
alert('taphold!');
tap=false;
},500);
});
$("body").on("touchend", my_selector, function() {
if(tap) alert('tap');
else tap=true;
clearTimeout(timer);
});
It successfully disables the default taphold event and context menu doesn't appear. However it also disables useful events, such as swipe. The links are in a vertical menu and the menu is higher than the screen, so a user has to scroll it. If he tries to scroll, starting on an anchor, it won't scroll, it will alert 'tap!'
Any ideas how could I disable taphold default or how could I fix this code so it disables only tap events and leave default swipe events enabled?
Edit: Now I thought about setting a timeout, if the pointer is in the same place for lets say 100ms, then prevent default action. However e.preventDefault(); doesn't work inside setTimeout callback.
So now I'm just asking about the simplest example. Can I prevent default actions after certain amount of time has passed (while the touch is still there).
And this is my whole problem in a fiddle. http://jsfiddle.net/56Szw/593/
This is not my code, I got this from http://www.gianlucaguarini.com/blog/detecting-the-tap-event-on-a-mobile-touch-device-using-javascript/
Notice that while swiping the box up and down, scrolling doesn't work.
I got the solution. It was so simple! I had no idea there's an oncontextmenu event. This solves everything:
$("body").on("contextmenu", my_selector, function() { return false; });
For an <img> I had to use event.preventDefault() instead of return false.
document.querySelector('img').addEventListener('contextmenu', (event) => {
event.preventDefault();
}

In iOS 7 Safari, how do you differentiate popstate events via edge swipe vs. the back/fwd buttons?

In iOS 7 Safari there are now two ways to navigate back/forward -- using the traditional back/forward button arrows at the bottom or by swiping from the edge of the screen. I'm using an animation to transition between pages in my ajax app, but I don't want to fire that transition if users are navigating via the edge swipe, because that is an animation itself.
However, the popstate event objects appear to be identical for both types of navigation -- is there any way to differentiate between these two types of user navigations so we can respond accordingly?
UPDATE: I was able to use (what appears to be) a bug in iOS7 Safari to detect correctly the edge swipe vs. back button tap. The bug is that the touchend event is not triggered (until the next touch event) when using the edge swipe (but touchstart and touchmove are). So I set a shouldAnimate flag and disable it on touchmove -- then if the flag is disabled and the popstate occurs, I know it's an edge swipe.
It's correct 99% of the time -- the only time where it could potentially fail is when a user edge-swipes partially and but then lets go and lets the current page snap back into place (at which point my flag would still be disabled) and then taps the back button (which fires no touch events). To handle that last [edge] case I set a timer on touchmove to re-enable the flag after 50ms.
Yes it's "dirty" but for now it gets me what I want in almost every case so I'm ok with it -- until Apple fixes the bug, but hopefully they'll also provide an indicator in the popstate event object that tells us what kind of navigation it is.
You can track edge drag navigation by monitoring the touch events. If the user starts dragging within a certain threshold of the edge of their screen, it will trigger an edge drag navigation transition.
I wrote an extended explanation of how to monitor and act on this using React code here: https://gist.github.com/MartijnHols/709965559cbdb6b241c12e5866941e69. The essential detection part can be achieved in regular JavaScript, like so:
window.isEdgeDragNavigating = false
const IOS_EDGE_DRAG_NAVIGATION_THRESHOLD = 25
let timer
const handleTouchStart = (e) => {
if (
e.touches[0].pageX > IOS_EDGE_DRAG_NAVIGATION_THRESHOLD &&
e.touches[0].pageX <
window.innerWidth - IOS_EDGE_DRAG_NAVIGATION_THRESHOLD
) {
return
}
window.isEdgeDragNavigating = true
if (timer) {
clearTimeout(timer)
}
}
const handleTouchEnd = () => {
timer = setTimeout(() => window.isEdgeDragNavigating = false, 200)
}
document.addEventListener('touchstart', handleTouchStart)
document.addEventListener('touchend', handleTouchEnd)
Short and sad answer: No. This back/forward-swipes are not propagated to the actual page but happen on an OS-level.

How do I catch taps but not scrolling in Javascript in Android?

I'm making an Javascript web app and I can't for the life of me get the touchstart event to fire. I get the touchmove and touchend events no problem. This is a problem because as I see it the best way to distinguish between a tap and a scrolling motion is to zero a counter on the touchstart event, update it at touchmove and then compare it at touchend. I'm doing this so I can do some action at the end of tap but not a scroll. For instance, it would be very confusing if a page opened for an item in a listed after you finished scrolling down that list, but it would be nice to be able to tap on an item to open its page.
This is what I have:
// FIXME: this doesn't seem to ever fire
el.addEventListener('touchstart', function(e) {
// make sure that at the start of every touch we're not considered to be moving
alert("Touch starting");
app.__touchMoving = 0;
}, false);
el.addEventListener('touchmove', function(e) {
app.__touchMoving++;
}, false);
el.addEventListener('touchend', function(e) {
alert("Touch ended. We moved beforehand this many times: " + app.__touchMoving);
// if we are moving
if (app.__touchMoving > 0) {
// stop, since we're dragging, not tapping
return false;
}
// else we're no longer moving, so it was a tap
}
I never see the touchstart alert. If I scroll the touchend will fire and app__touchMoving will have some sort of decent value. On a side note, I've noticed that sometimes the touchend will seem to fire multiple times.
Am I missing something basic here? Plenty of people say that this should work just fine on Android (and iPhone) yet the first listener never seems to fire.
Update: I should mention that I've been testing on a Samsung Galaxy S running Android 2.1.
I don't know if u can use it: iScroll

Categories