Timed long press in Javascript within jQuery phonegap app - javascript

There is a good example for doing long press in Javascript here: Long Press in JavaScript?
But it does not provide for knowing the duration of the press.
If I want to do different things based on the length of the press I cant use the pattern in that post.
I was trying to do something similar by saving current time in a variable on('mousedown')
and then calculating the time difference on('mouseup').
this works fine within a normal Javasript page in a "normal" browser.
However within my phonegap app something happens,
looks like the mouseup event is not being called if the finger is kept on the screen for a long duration (say 5 sec..).
Is this some native mobile browser behavior? Can I override it somehow?
I am using plain jQuery not jQuery mobile.
Any ideas anyone?

You could have a look at how the taphold and vmouseup (handleTouchEnd() line 752) events are implemented in jQuery mobile source code.
Since it is already tested and implemented I'd suggest to use jquery mobile instead of jquery and modify (since it already handles all the 'quirks' related each mobile browser), and change the code as you need.

You can check the time to identify Click or Long Press [jQuery]
function AddButtonEventListener() {
try {
var mousedowntime;
var presstime;
$("button[id$='" + buttonID + "']").mousedown(function() {
var d = new Date();
mousedowntime = d.getTime();
});
$("button[id$='" + buttonID + "']").mouseup(function() {
var d = new Date();
presstime = d.getTime() - mousedowntime;
if (presstime > 999/*You can decide the time*/) {
//Do_Action_Long_Press_Event();
}
else {
//Do_Action_Click_Event();
}
});
}
catch (err) {
alert(err.message);
}
}

Note that this solution is usefull if you do not use jQuery Mobile for some reason.
I used the article Fast Touch Event Handling and just added a piece of code
$.event.special.tap = {
distanceThreshold: 10,
timeThreshold: 350,
setup: function () {
var self = this,
$self = $(self);
// Bind touch start
$self.on('touchstart', function (startEvent) {
// Save the target element of the start event
var target = startEvent.target,
touchStart = startEvent.originalEvent.touches[0],
startX = touchStart.pageX,
startY = touchStart.pageY,
threshold = $.event.special.tap.distanceThreshold,
timeout,
expired = false;
function timerFired() {
expired = true;
}
function removeTapHandler() {
clearTimeout(timeout);
$self.off('touchmove', moveHandler).off('touchend', tapHandler).off('touchcancel', removeTapHandler);
};
function tapHandler(endEvent) {
removeTapHandler();
if (target == endEvent.target) {
if (expired) {
$.event.simulate('longtap', self, endEvent);
} else {
$.event.simulate('tap', self, endEvent);
}
}
};
// Remove tap and move handlers if the touch moves too far
function moveHandler(moveEvent) {
var touchMove = moveEvent.originalEvent.touches[0],
moveX = touchMove.pageX,
moveY = touchMove.pageY;
if (Math.abs(moveX - startX) > threshold || Math.abs(moveY - startY) > threshold) {
removeTapHandler();
}
};
// Remove the tap and move handlers if the timeout expires
timeout = setTimeout(timerFired, $.event.special.tap.timeThreshold);
// When a touch starts, bind a touch end and touch move handler
$self.on('touchmove', moveHandler).on('touchend', tapHandler).on('touchcancel', removeTapHandler);
});
}
};
So, now I have a tap and a longtap events

Related

"Wheel" event triggers more than once despite using throttle

i try to do something like this:
function throttle(fn, wait) {
var time = Date.now();
return function() {
if ((time + wait - Date.now()) < 0) {
fn();
time = Date.now();
}
}
}
function callback() {
//something
}
something.addEventListener("wheel", throttle(callback, 500));
When I use mousewheel it seems to work nice and triggers only once. The issue is when I use Macbook's touchpad this event triggers (depending on swipe's length) 1, 2 or 3 times at once. What's a problem?
Your code is fine, the problem is that when you "wheel" with a touchpad, you trigger the wheel event a lot, especially a lot of very small values.
For example, if you try to scroll this page with a touchpad, you will notice the smoothness of the scroll. That's because many events are fired with a degressive value.
A throttle is a good start but not enough. An upgrade would be to dismiss wheel events with a very small delta value, like this:
function throttle(fn, wait) {
var time = Date.now();
return function(event) {
// we dismiss every wheel event with deltaY less than 4
if (Math.abs(event.deltaY) < 4) return
if ((time + wait - Date.now()) < 0) {
fn(event);
time = Date.now();
}
}
}
function callback(event) {
// something
}
something.addEventListener("wheel", throttle(callback, 500));
It won't be "perfect" but close.
If you want a perfect result, some advanced maths is necessary. And when I mean advanced, I mean I would myself need one week or two full-time to implement it cleanly across all devices.
If you want to control the wheel to scroll from a screen A to a screen B, you should check out the css scroll snapping property.

When does a click become a hold?

I'm making a web app where a button's behavior is different if the user clicks vs holds the button. I have been experimenting with different timings and it got me wondering if there is any established standard for this kind of thing.
For clarification: I am wondering if there is an exact timing that is standard. Below is the code I am using with 150ms being the threshold for a hold.
function onMouseDown()
{
var holdTimeout = setTimeout(function()
{
//Hold code (also cancels click event)
}, 150);
var cancelHold = function()
{
clearTimeout(holdTimeout);
};
window.onmouseup = cancelHold;
}
function onClick()
{
//Click code
}
Answering exactly your question, hold becomes click. You could set the click event (it's release in fact), inside the mousedown event. Run the code below and try holding and release the mouse button.
document.getElementById("click").addEventListener('mousedown', (e) => {
var i = 0;
var int = setInterval(() => {
console.log("hold " + i++);//<-- actions when we hold the button
}, 200)
document.getElementById("click").addEventListener("click", () => {
clearInterval(int);
console.log("release")//<-- actions when we release the button
})
});
<div id="click">click</div>
In this case, if we hold the button less that 200 milliseconds, just the click (release) event is fired.

Disable Predictive Scrolling - Mousewheel (OnScroll) Event fires too often (Touchpad)

I am executing Javascript onScroll.
My code works great with any normal computer mouse, but when I use my notebook's touchpad, I encounter the following situation:
my mouse fires (about 1 to 8) mousewheel events while the finger is moving the wheel.
my touchpad fires a lot more (~60) mousewheel events while the two fingers are touching the pad and continues to fire after my fingers are up in the air again.
I know this behavior from mobile touch devices. The Feature is called "Predictive Touch" - The Scrolling continues if your finger movement had enough acceleration before lifting it up.
I think the touchpad drivers are setting this "smooth scrolling" behavior.
To debug this case, I have used the following code:
/* Handle Mouse-Wheel Scrolling */
var lastChange = +new Date();
$(window).bind('mousewheel', function(e){
console.log("mw");
if(+new Date() - lastChange > 1000){
console.log("mw allowed");
if(e.originalEvent.wheelDelta > 0) {/*go to previous*/}
else{ /*go to next*/}
lastChange = +new Date();
}
return false;});
This is a simple code that "allows" a mouse-scrolling-event every second.
If I make a fast touchpad-scroll, the mousewheel event is fired ~300 times. The one-second-condition is letting 3 events happen. My fingers were on the touchpad for far less than a second.
With this test, I discovered that the mousewheel events are still fired (almost continuously for 3 seconds), even when my fingers are already off the touchpad.
Is there a Javascript function or a workaround / trick / hack to avoid this behavior?
Something like a onTouchEnd event for touchpads, maybe?
To achieve this, you'd have to distinguish between mouse scroll events and touchpad events, which is not (yet) possible using JavaScript. It was already asked in question How to capture touch pad input.
Pointer Events are currently in state of Editor's Draft and not yet supported by any browser. See also touch events docs on MDN.
EDIT: This doesn't appear to work for trackpads. Once they are widely supported, this could be implemented using Touch Events, specifically the Touch End event. By tracking when the finger leaves the trackpad, you can prevent the page scrolling at that particular point.
https://jsfiddle.net/gLkkb5z0/3/
(function(){
var special = jQuery.event.special,
uid1 = 'D' + (+new Date()),
uid2 = 'D' + (+new Date() + 1);
special.scrollstart = {
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
} else {
evt.type = 'scrollstart';
jQuery.event.handle.apply(_self, _args);
}
timer = setTimeout( function(){
timer = null;
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid1, handler);
},
teardown: function(){
jQuery(this).unbind( 'scroll', jQuery(this).data(uid1) );
}
};
special.scrollstop = {
latency: 300,
setup: function() {
var timer,
handler = function(evt) {
var _self = this,
_args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout( function(){
timer = null;
evt.type = 'scrollstop';
jQuery.event.handle.apply(_self, _args);
}, special.scrollstop.latency);
};
jQuery(this).bind('scroll', handler).data(uid2, handler);
},
teardown: function() {
jQuery(this).unbind( 'scroll', jQuery(this).data(uid2) );
}
};
})();
Demo
Taken from http://james.padolsey.com/javascript/special-scroll-events-for-jquery/

JavaScript touch event updates for force or radius

JavaScript touch events contain properties for radius and force. Unfortunately it appears that events aren't generated when either property changes. Events are only triggered for things like touch start, move or end. Can anyone think of a way to get more updates on change of touch size?
Currently to get radius updates I have to wiggle my finger to trigger the touch move event, but I would prefer a software solution.
I had the same issue and then discovered this blog post: http://blog.framerjs.com/posts/prototyping-3D-touch-interactions.html
In a nutshell, we need to use touchstart event to capture the touch event, assign the event to a variable and then use setInterval to get the force value:
var el = document.getElementById('myElement');
var currTouch = null;
var currTouchInterval = null;
attachListeners = function () {
el.addEventListener('touchstart', enterForceTouch, false);
el.addEventListener('touchend', exitForceTouch, false);
}
enterForceTouch = function (evt) {
evt.preventDefault();
currTouch = evt;
currTouchInterval = setInterval(updateForceTouch, 10); // 100 times per second.
}
updateForceTouch = function() {
if (currTouch) {
console.log(currTouch.touches[0].force); // Log our current force value.
}
}
exitForceTouch = function (evt) {
evt.preventDefault();
currTouch = null;
clearInterval(currTouchInterval);
}
attachListeners();

windows 8 javascript app "tapped" event not always working

i´m testing a Windows 8 app for a touch pannel.
for now i just want to handle the "tapped" events over the screen.
I´ve tried it with a "MSGesture" object, but the tap events fires the 60%-80% of times. It detects all the "pointerdown" events, but not all of them also trigger a tap event.
Now i´ve tried the same with a "GestureRecognizer" object and the problem remains the same: all the "pointerdown" events fire ok, but not all of them are recognize as tap events.
The Code:
gr = new Windows.UI.Input.GestureRecognizer();
gr.gestureSettings = Windows.UI.Input.GestureSettings.tap | Windows.UI.Input.GestureSettings.doubleTap;
gr.addEventListener("tapped", onTap);
var mainGrid = document.getElementById("mainGrid");
mainGrid.addEventListener("pointerdown", onPointerDown, false);
mainGrid.addEventListener('pointermove', processMove, false);
mainGrid.addEventListener('pointerup', processUp, false);
mainGrid.addEventListener('pointercancel', processUp, false);
function onPointerDown(e) {
count++;
counter.innerHTML = "Count: " + count;
// Get the current PointerPoint
var pp = e.getCurrentPoint(e.currentTarget);
// Feed the PointerPoint to GestureRecognizer
gr.processDownEvent(pp);
}
function processMove(e) {
// Get the current PointerPoint
var pps = e.getIntermediatePoints(e.currentTarget);
// Feed the PointerPoint to GestureRecognizer
gr.processMoveEvents(pps);
}
function processUp(e) {
// Get the current PointerPoint
var pp = e.getCurrentPoint(e.currentTarget);
// Feed the PointerPoint to GestureRecognizer
gr.processUpEvent(pp);
}
function onTap(e) {
count2++;
counter2.innerHTML = "Count2: " + count2;
}
As you can see, there are "counter" and "counter2" DOM elements, those are just used for showing a counter that increments each time each event is fired.
Could it be a Windows 8 App performance bug?
Thanks for your help!

Categories