Mobile firefox click and hold events - javascript

I am trying to make simple webapp where an element can be clicked or hold to call different function.
$(document).on("click",'.element', function() {
clickFunction();
});
let timeoutId = 0;
$(document).on('pointerdown','.element', function() {
timeoutId = setTimeout(holdFunction, 500);
}).on('pointerup pointerleave', '.element', function() {
clearTimeout(timeoutId);
});
And it was tested and worked fine on Chrome, Firefox and mobile Chrome.
The problem is that on mobile Firefox the 'pointerdown' event is not called at all and i don't really know why.

It turns out that mobile firefox browser is not supporting pointer events.
To check if browser supports pointer events simply use
if (window.PointerEvent)
On mobile firefox one should use touch events (touchstart, touchend, etc.)
It is also worth mentioning that if you don't want to call holdFunction while holding element and scrolling the the same time, the code for pointer events given in the question will work fine but when using touch events you probably will need additional check if window was scrolled
$(window).scroll(function(event){clearTimeout(timeoutId);});

Related

mouseleave event for document, with consideration for Chrome bug

I need to safely detect when the mouse leaves the window. I have jQuery included, so normally this would be fine:
$(document).on("mouseleave", function(event) {
doSomething();
});
However, there is a major bug in Chrome currently where this mouseleave function fires randomly when clicking in the element.
Normally, there's an easy work around for this:
$("#some-id").on("mouseleave", function(event) {
var e = event.originalEvent;
if (!e.relatedTarget || !e.toElement) {
// BUG in Chrome
return;
}
doSomething();
}
However, this doesn't work for document, or for any element when the mouse leaves the window, since in this case, e.relatedTarget and e.toElement are null exactly like when the bug occurs. So I'm trying to come up with something that will be able to safely determine when Chrome is acting up, and when the mouseleave event actually should fire.
Update: Just tried an approach with mouseout instead. The same chrome bug affects this event too, so no good. :/
UPDATE: this was apparently fixed, the Chrome issue was marked "fixed", and my tests show that the current version of Chrome no longer has the issue. :D
For now, this is the solution I came up with. Hopefully someday this major bug in Chrome gets enough attention to get a fix.
In the event that mouseleave occurs as a bug, it is usually almost immediately by the mouseenter event. So what I did here was just wait 1/10 of a second after the mouseleave event to make sure it's not a Chrome fluke.
var documentMouseLeaveTimeout = null;
$(document).on("mouseleave", function() {
documentMouseLeaveTimeout = setTimeout(function() {
doSomething();
}, 100);
});
$(document).on("mouseenter", function() {
clearTimeout(documentMouseLeaveTimeout);
});
Still hoping there's a better answer.
UPDATE: this was apparently fixed, the Chrome issue was marked "fixed", and my tests show that the current version of Chrome no longer has the issue. :D

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.

Is there any way to use window.onbeforeunload on Mobile Safari for iOS devices?

Looks like Apple has disabled the window.onbeforeunload event for iOS devices (iPhone, iPad, iPod Touch). Unfortunately I can't find any documentation as to why this event doesn't work in Mobile Safari.
Does anyone know if there's a reliable alternative to this function? Android's browser appears to support it just fine, and the Safari desktop application also supports the onbeforeunload event without issue.
I see that it's an old question, but i faced this problem recently.
I'm using window.unload and it works fine in ios browsers (although if you look at Apple documentation it seems to be deprecated and they recommend to use document.pagehide)
If you really need it, you cant just get all links, forms and DOM objects that have a handler changing the url and make those wait until you've done what you want.
For the links, you get them by getElementsByTagName, check if the href starts with anything but a # and just add your onbeforeunload function add onclick (which will be invoked before the href is looked at).
Same for the forms but with onsubmit.
And finaly, for the elements changing the href with JavaScript, you should make sure when you add the lsitener that you call your onbeforeunlaod function (or, if you use DOM0 or DOM1 listeners, you can just add some class and then use a global script that checks all elements with the class and adds it to the event listener with a closure.
But you should normaly be able to avoid the use of this event (probably using cookies to store the thing you wanted to send every x seconds and allowing to, in the worst case, have a look at it next time the user loads a page and, in the best case, be able to send an Ajax request at onbeforeunload or onunload which, even if it sends only the http headers, woudl allow you to get what you want).
Based on Xavier's answer, I devised a solution along these lines:
function doStuff() {
// here goes your logic
}
function isSafariMobile() {
return navigator && /Safari/.test(navigator.userAgent) && /iPhone|iPad/.test(navigator.userAgent)
}
function addWatcherToLinks(baseNode) {
if (!baseNode || !baseNode.querySelectorAll) { return; } // ignore comments, text, etc.
for (const link of baseNode.querySelectorAll("a")) {
link.addEventListener('click', doStuff);
}
for (const form of baseNode.querySelectorAll("form")) {
form.addEventListener('submit', doStuff);
}
}
// ...when the page loads...
// we watch the page for beforeunload to call doStuff
// Since Safari mobile does not support this, we attach a listener (watcher) to each link and form and then call doStuff.
// Also, we add such a watcher to all new incoming nodes (DOMNodeInserted).
if (isSafariMobile()) {
addWatcherToLinks(document);
window.addEventListener("DOMNodeInserted", (event) => { addWatcherToLinks(event.target); }, false);
} else {
window.addEventListener('beforeunload', doStuff);
}
This solution has some limitations. The biggest one is that it attaches itself to all forms and all links. Sometimes this might not be desired. If you need it you can skip some nodes (e.g. mark them with a particular data- attribute).
I was having the same problem. it seems safari browser in iphone triggers only focus and blur events and almost every other event is not triggered, e.g.(pagehide, pageshow, visibility change) but the good news is focus and blur event are supported and triggered on iphone, ipad & android mobiles as well.
window.addEventListener('focus', function(){
// do stuff
});
window.addEventListener('blur', function(){
// do stuff
});
hope this helps anyone.

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

window.parent jQuery selector not working in IE8

This code below works fine
$('html').bind('mousewheel', function(event, delta) {
window.parent.scrollBy(-120 * delta,0);
return false;
});
but this one doesn't, can anyone tell me why. I'd love to know.
$(window.parent).bind('mousewheel', function(event, delta) {
window.parent.scrollBy(-120 * delta,0);
return false;
});
I'd like to clarify that the window selector doesn't work in this case either.
The problem may be that jQuery's event handler wrapper must use window.event to retrieve the current event in IE. If you set a handler from window A on an event in window B, the script in window A will be looking at window A's window.event, whilst the event is actually occurring on window B.
But there may be more issues than that, too. Cross-window/frame scripting is fraught with difficulties and jQuery is not particularly designed to work around them. To make jQuery work properly cross-frame you will generally need an instance of jQuery in both windows, and you should only use the corresponding instance of jQuery ($) to interact with each window.
eta re comment:
OK, having looked into mousewheel further, I don't know how your code can be working in Firefox (it certainly doesn't for me). Firefox doesn't support mousewheel events at all; instead it supports DOMMouseScroll events. Also for the other browsers that support mousewheel, it should be bound to a DOM Node rather than the window. So I guess what you're looking for is:
if ('MouseScrollEvent' in window) {
$(document).bind('DOMMouseScroll', function(event) {
return scroll(event.detail*-40);
});
} else {
$(document).bind('mousewheel', function(event) {
return scroll(event.wheelDelta);
});
}
function scroll(d) {
window.scrollBy(-d, 0);
return false;
};
(However in WebKit this will stop scrolling when the mouse moves out of the horizontal area corresponding to the viewport width. You may prefer to bind the events to the wider element like the div, if it fills the browser.)

Categories