Setinterval with exponential time decrease - javascript

I've got a mousedown event with a setinterval. I want the time of intervals to be variable. So the first one is 500, the second one 500/2 = 250, etc. Any tips?
$plus.mousedown(function(e) {
increment(20)
timeout = setInterval(function(){
increment(20)
}, 500);
});
$(document).mouseup(function(){
clearInterval(timeout);
return false;
});
Cheers!
EDIT: sorry for the ambiguity. I want the time of interval to change during the mousedown. So while the mousedown is being performed the intervaltime should change. So not by every single mouse click but with every continuous click, and then reset it again.

You can't really do this with setInterval() unless you keep clearing for a delay change, so you might as well write a wrapper around setTimeout() to accomplish something similar:
function easingTimeout(delay, fn)
{
var id,
invoker = function() {
fn();
delay = Math.floor(delay / 2);
if (delay) {
id = setTimeout(invoker, delay);
} else {
id = null;
}
}
// start it off
id = setTimeout(invoker, delay);
return {
clear: function() {
if (id) {
clearTimeout(id);
id = null;
}
}
}
To use:
var timeout;
$plus.mousedown(function(e) {
increment(20);
timeout = easingTimeout(500, function() {
increment(20);
});
});
$(document).mouseup(function(){
timeout.clear();
return false;
});

This solution does not depend on jQuery:
var timeoutInterval = 500;
var mousedown = false;
function incrementAndWait() {
if (!mousedown) return;
increment(20);
timeout = setTimeout(incrementAndWait, timeoutInterval);
timeoutInterval /= 2;
}
document.onmousedown = function() {
timeoutInterval = 500; // Reset to 500 to allow multiple mousedown/mouseup
mousedown = true;
incrementAndWait();
};
document.onmouseup = function() {
mousedown = false;
}
You can add console.log((new Date).getTime(), 20); to the incrementAndWait method to see the numbers going on the console. Something fun to play with :)

Related

How to detect "scrollstop" event in a browser

I know that there is no scrollstop event in an official browser DOM documentation but how to detect it?
The most logical way to do this is to listen for a timeout.
var timer = null;
var timeout = 150; //ms
window.addEventListener('scroll', function() {
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
// do something
}, timeout);
}, false);
If it is still not scroll after 150ms it means the scroll has stopped!
You can change this timeout as you see fit. Again, I think the most logical thing is to choose a value between 150 and 250ms.
var timer = null;
var timeout = 150; //ms
window.addEventListener('scroll', function() {
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
console.log('Scroll Stopped');
}, timeout);
}, false);
html,
body {
padding-top: 100rem;
}

Selective timeout based events handling: immediate first, debounce next

Let's say there are random sequences of external actions (e.g. scroll events). I need to handle the first action immediately, then dismiss all actions occurred with intervals less than some given delta, and then handle the next one which should be delayed for that delta. Further actions should be processed in the same manner.
This looks like a combination of debounce-immediate and simple debounce. I prepared a diagram to demonstrate the idea.
What is the best solution/approach here? I wonder if there is some ready-made pattern...
UPDATE
I would like to thank all participants! For the research I created plunker with four five different realizations suggested in answers: https://plnkr.co/N9nAwQ.
const handler = [
processEvent, // normal
debounceNext(processEvent, DELAY), // dhilt
makeRateLimitedEventHandler(DELAY, processEvent), // user650881
debounceWithDelay(processEvent, DELAY, 0), // willem-dhaeseleer
_.debounce(processEvent, DELAY, {leading: true}) // lodash debounce + leading,
debounceish(DELAY, processEvent) //Mikk3lRo
];
A great news was the Lodash has a leading-flag debounce implementation which satisfies the issue (thanks to Willem D'Haeseleer). And here is the cool demo from Mikk3lRo' answer, he also provided some useful synthesis.
I investigated the sources and the results: form just visual point to memory allocation stuff... I didn't find any performance issues, and the views seem okey. So the ultima ratio was the code itself. All sources were converted to ES6 (as you can see in Plunker) for I can compare them fully. I excluded my own try (it is a bit excessive, despite I like how it looks). The timestamp version is very interesting! The postDelay version's nice, though it wasn't a requested feature (so that snippet demo has double delay for two lodash demos).
I decided not to have a lodash dependency (in other way I certainly would use lodash debounce with leading option), so I chose debounceish by Mikk3lRo.
PS I would like to share that little bounty (unfortunately there is no such an option) or even take some more scores from my reputation for it (but not 200, is too much and would be unfair to the winner which would have only 100). I even can't vote twice... Nevermind.
A very simple solution in vanilla JS using a single timer:
function debounceish(delta, fn) {
var timer = null;
return function(e) {
if (timer === null) {
//Do now
fn(e);
//Set timer that does nothing (but is not null until it's done!)
timer = setTimeout(function(){
timer = null;
}, delta);
} else {
//Clear existing timer
clearTimeout(timer);
//Set a new one that actually does something
timer = setTimeout(function(){
fn(e);
//Set timer that does nothing again
timer = setTimeout(function(){
timer = null;
}, delta);
}, delta);
}
};
}
function markEvt(e) {
var elm = document.createElement('div');
elm.style.cssText = 'position:absolute;background:tomato;border-radius:3px;width:6px;height:6px;margin:-3px;';
elm.style.top = e.clientY + 'px';
elm.style.left = e.clientX + 'px';
document.body.appendChild(elm);
}
document.addEventListener('click', debounceish(2000, markEvt));
<p>Click somewhere (2000ms delta) !</p>
Comparing 6 proposals using the same type of visualization:
var methods = {
default: function(delay, fn) {
return fn;
},
dhilt_debounceNext: (delay, cb) => {
let timer = null;
let next = null;
const runTimer = (delay, event) => {
timer = setTimeout(() => {
timer = null;
if(next) {
next(event);
next = null;
runTimer(delay);
}
}, delay);
};
return (event) => {
if(!timer) {
cb(event);
}
else {
next = cb;
clearTimeout(timer);
}
runTimer(delay, event);
}
},
Mikk3lRo_debounceish(delta, fn) {
var timer = null;
return function(e) {
if (timer === null) {
//Do now
fn(e);
//Set timer that does nothing (but is not null until it's done!)
timer = setTimeout(function(){
timer = null;
}, delta);
} else {
//Clear existing timer
clearTimeout(timer);
//Set a new one that actually does something
timer = setTimeout(function(){
fn(e);
//Set timer that does nothing again
timer = setTimeout(function(){
timer = null;
}, delta);
}, delta);
}
};
},
user650881_makeRateLimitedEventHandler: function(delta_ms, processEvent) {
var timeoutId = 0; // valid timeoutId's are positive.
var lastEventTimestamp = 0;
var handler = function (evt) {
// Any untriggered handler will be discarded.
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = 0;
}
var curTime = Date.now();
if (curTime < lastEventTimestamp + delta_ms) {
// within delta of last event, postpone handling
timeoutId = setTimeout(function () {
processEvent(evt);
}, delta_ms);
} else {
// long enough since last event, handle now
processEvent(evt);
}
// Set lastEventTimestamp to time of last event after delta test.
lastEventTimestamp = Date.now();
};
return handler;
},
Willem_DHaeseleer_debounceWithDelay: (delay, func) => {
let postDebounceWait;
let timeOutLeading = false;
const debounced = _.debounce((...args) => {
// wrap the handler so we can add an additional timeout to the debounce invocation
if (timeOutLeading) {
/*
for the first invocation we do not want an additional timeout.
We can know this is the leading invocation because,
we set timeOutLeading immediately to false after invoking the debounced function.
This only works because the debounced leading functionality is synchronous it self.
( aka it does not use a trampoline )
*/
func(...args);
} else {
postDebounceWait = setTimeout(() => {
func(...args)
}, delay);
}
}, delay, {leading: true});
return (...args) => {
// wrap the debounced method it self so we can cancel the post delay timer that was invoked by debounced on each invocation.
timeOutLeading = true;
clearTimeout(postDebounceWait);
debounced(...args);
timeOutLeading = false;
}
},
Willem_DHaeseleer_lodashWithLeading: (delta, cb) => {
return _.debounce(cb, delta * 2, {leading: true});
},
Javier_Rey_selfCancelerEventListener: function (delta, fn) {
return function(ev) {
var time = new Date().getTime();
if (ev.target.time && time - ev.target.time < delta) {return;}
ev.target.time = time;
fn(ev);
};
},
};
var method_count = 0;
var colors = ['grey', 'tomato', 'green', 'blue', 'red', 'orange', 'yellow', 'black'];
function markEvt(method) {
var style = 'position:absolute;border-radius:3px;width:6px;height:6px;margin:-3px;';
style += 'background:' + colors[method_count] + ';';
if (method_count > 0) {
style += 'transform:rotate(' + Math.floor(360 * method_count / (Object.keys(methods).length - 1)) + 'deg) translateY(-8px);';
}
var elm = document.createElement('div');
elm.innerHTML = '<span style="width:.8em;height:.8em;border-radius:.4em;display:inline-block;background:' + colors[method_count] + '"></span> ' + method;
document.body.appendChild(elm);
method_count++;
return function(e) {
elm = document.createElement('div');
elm.style.cssText = style;
elm.style.top = e.clientY + 'px';
elm.style.left = e.clientX + 'px';
document.body.appendChild(elm);
};
}
for (var method in methods) {
document.addEventListener('click', methods[method](2000, markEvt(method)));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Note that I needed to make minor adjustments to some of the methods to get a common interface. Adapting Cully's answer took more effort than I was willing to put in considering the comments suggest it doesn't do what the OP wants anyway.
It should be pretty clear that Javier Rey's approach behaves completely differently from the rest. Dhilt, user650881 and my own methods seem consistent. Both of Willem D'Haeseleer's methods have double the delay (and other subtle differences), but seem to behave consistently too. As far as I understand the double delay is completely intentional, though that is not how I understand the OP.
I would say that Willem D'Haeseleer's lodash method is without a doubt the simplest - if you already use lodash that is. Without external dependencies my method is IMO simplest - but I may be biased on that one ;)
You might track the last event time and only create a timer event when a follow-up check is required.
function makeRateLimitedEventHandler(delta_ms, processEvent) {
var timeoutId = 0; // valid timeoutId's are positive.
var lastEventTimestamp = 0;
var handler = function (evt) {
// Any untriggered handler will be discarded.
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = 0;
}
var curTime = Date.now();
if (curTime < lastEventTimestamp + delta_ms) {
// within delta of last event, postpone handling
timeoutId = setTimeout(function () {
processEvent(evt);
}, delta_ms);
} else {
// long enough since last event, handle now
processEvent(evt);
}
// Set lastEventTimestamp to time of last event after delta test.
lastEventTimestamp = Date.now();
};
return handler;
}
var DELTA_MS = 5000;
var processEvent = function (evt) { console.log('handling event'); };
el.addEventHandler('some-event', makeRateLimitedEventHandler(DELTA_MS, processEvent));
The behavior in your visual is no different then the standard lodash debouncing behavior with the leading option, the only difference is that your only displaying half of the delta instead of the full delta.
Therefore, your solution can be as simple as this.
_.debounce(cb, delta * 2, {leading: true});
https://lodash.com/docs/4.17.4#debounce
If you want the last delay to be longer, you can solve that by wrapping both the debounced method and the handler. That way you can set the timeout in the handler, and cancel it in the debounce wrapper.
You do have to check if the current invocation was the leading one in order to not add the timeout in that case.
It could look like this:
const _ = require('lodash');
const bb = require('bluebird');
function handler(arg) {
console.log(arg, new Date().getSeconds());
}
const debounceWithDelay = (func, delay, postDelay) => {
let postDebounceWait;
let timeOutLeading = false;
const debounced = _.debounce((...args) => {
// wrap the handler so we can add an additional timeout to the debounce invocation
if (timeOutLeading) {
/*
for the first invocation we do not want an additional timeout.
We can know this is the leading invocation because,
we set timeOutLeading immediately to false after invoking the debounced function.
This only works because the debounced leading functionality is synchronous it self.
( aka it does not use a trampoline )
*/
func(...args);
} else {
postDebounceWait = setTimeout(() => {
func(...args)
}, postDelay);
}
}, delay, {leading: true});
return (...args) => {
// wrap the debounced method it self so we can cancel the post delay timer that was invoked by debounced on each invocation.
timeOutLeading = true;
clearTimeout(postDebounceWait);
debounced(...args);
timeOutLeading = false;
}
};
const debounceDelay = debounceWithDelay(handler, 50, 2000);
(async function () {
console.log(new Date().getSeconds());
debounceDelay(1);
debounceDelay(2);
debounceDelay(3);
debounceDelay(4);
await bb.delay(3000);
debounceDelay(5);
await bb.delay(3000);
debounceDelay(6);
debounceDelay(7);
debounceDelay(8);
})();
Runnable script:
Here's something that I think works the way you described. If not, it's at least something to go off of.
// set up the event bus
const start = getMilli()
const bus = createBus()
bus.on('event', e => console.log(`[${getPassage(start)}] [${e}] original bus: saw event`))
const wrappedBus = wrapBus(1600, 'event', bus)
wrappedBus.on('event', e => console.log(`[${getPassage(start)}] [${e}] wrapped bus: saw event`))
wrappedBus.on('skipped', e => console.log(`[${getPassage(start)}] [${e}] skipped by wrapped bus`))
wrappedBus.on('last before interval', e => console.log(`[${getPassage(start)}] [${e}] this was the last event before the end of the interval`))
wrappedBus.on('interval tick', _ => console.log(`[${getPassage(start)}] interval tick`))
// trigger events on the bus every so often
let totalTime = 0
const intervalTime = 300
setInterval(() => {
totalTime += intervalTime
bus.trigger('event', totalTime)
}, intervalTime)
function getMilli() {
return (new Date()).getTime()
}
function getPassage(from) {
return getMilli() - from
}
// creates a simple event bus
function createBus() {
const cbs = {}
return {
on: (label, cb) => {
if(cbs.hasOwnProperty(label)) cbs[label].push(cb)
else cbs[label] = [cb]
},
trigger: (label, e) => {
if(cbs.hasOwnProperty(label)) cbs[label].forEach(f => f(e))
},
}
}
// creates a new bus that should trigger the way you described
function wrapBus(waitInterval, eventLabel, bus) {
const newBus = createBus()
let deliveredFirst = false
let gotIgnoredEvent = false
let lastIgnoredEvent = undefined
setInterval(() => {
// just here so we know when this interval timer is ticking
newBus.trigger('interval tick', null)
// push the last event before the end of this interval
if(gotIgnoredEvent) {
gotIgnoredEvent = false
deliveredFirst = false
newBus.trigger(eventLabel, lastIgnoredEvent)
newBus.trigger('last before interval', lastIgnoredEvent)
}
}, waitInterval)
bus.on(eventLabel, function(e) {
if(!deliveredFirst) {
newBus.trigger(eventLabel, e)
deliveredFirst = true
gotIgnoredEvent = false
}
else {
gotIgnoredEvent = true
lastIgnoredEvent = e
// this is here just to see when the wrapped bus skipped events
newBus.trigger('skipped', e)
}
})
return newBus
}
Here's my try:
const debounceNext = (cb, delay) => {
let timer = null;
let next = null;
const runTimer = (delay, event) => {
timer = setTimeout(() => {
timer = null;
if(next) {
next(event);
next = null;
runTimer(delay);
}
}, delay);
};
return (event) => {
if(!timer) {
cb(event);
}
else {
next = cb;
clearTimeout(timer);
}
runTimer(delay, event);
}
};
const processEvent = (event) => console.log(event);
const debouncedHandler = debounceNext(processEvent, 125);
myElement.addEventListener('scroll', debouncedHandler);

How would I toggle the state of a setInterval function in jQuery?

I want to be able to click a an element with an id of pause to start a count of the elements in a time object and if I re click the pause it will stop it and reclick start it exactly like the toggle feature in JQuery but with a setInteval function how would I go about doing this?
$("#pause").click(function(ffe) {
if(on == true) {
on = false
alert("on");
}
else {
on = true;
alert("off");
}
if(on == false) {
setInterval(function() {
$("#timet ul").append("<li>" + $("#time ul")
.children('li').length +"</li>");
}, 100);
}
else {
alert("Error");
}
});
A classic technique is to use a single master setInterval loop and simply use if..else logic to determine what needs to run. This is how a lot of javascript games work:
var on = true;
// Our master scheduler:
setInterval(function() {
if (on) {
$("#timet ul").append("<li>" + $("#time ul")
.children('li').length +"</li>");
}
}, 100);
// Code to handle the pause button
$("#pause").click(function(ffe) {
on = !on;
}
You can use the setTimeout function, if you want to run the function once, setInterval runs continuously, try the following:
var on = false;
$("#pause").click(function(ffe) {
if (on) {
on = false;
setTimeout(function() {
$("#timet ul").append("<li>" + $("#time ul")
.children('li').length +"</li>");
}, 100);
} else {
on = true;
}
});
You need to use .clearInterval() to stop the execution.
Here is the code: (THE WORKING DEMO)
$("#pause").click((function () {
var interId = null;
var $ul = $("#timet ul");
return function (e) {
if (interId) {
$(this).text("start");
clearInterval(interId);
interId = null;
} else {
$(this).text("pause");
interId = setInterval(function () {
$ul.append($('<li>').text($('li', $ul).length));
}, 100);
}
};
}()));​

Looping functions with timeout

I want to have two functions (an animation downwards and animation upwards) executing one after the other in a loop having a timeout of a few seconds between both animations. But I don't know how to say it in JS …
Here what I have so far:
Function 1
// Play the Peek animation - downwards
function peekTile() {
var peekAnimation = WinJS.UI.Animation.createPeekAnimation([tile1, tile2]);
// Reposition tiles to their desired post-animation position
tile1.style.top = "-150px";
tile2.style.top = "-150px";
peekAnimation.execute();
}
Function 2
// Play the Peek animation - upwards
function unpeekTile() {
var peekAnimation = WinJS.UI.Animation.createPeekAnimation([tile1, tile2]);
// Reposition tiles to their desired post-animation position
tile1.style.top = "0px";
tile2.style.top = "0px";
peekAnimation.execute();
}
And here's a sketch how both functions should be executed:
var page = WinJS.UI.Pages.define("/html/updateTile.html", {
ready: function (element, options) {
peekTile();
[timeOut]
unpeekTile();
[timeOut]
peekTile();
[timeOut]
unpeekTile();
[timeOut]
and so on …
}
});
You can do this using setTimeout or setInterval, so a simple function to do what you want is:
function cycleWithDelay() {
var delay = arguments[arguments.length - 1],
functions = Array.prototype.slice.call(arguments, 0, arguments.length - 1),
pos = 0;
return setInterval(function () {
functions[pos++]();
pos = pos % functions.length;
}, delay);
}
Usage would be like this for you:
var si = cycleWithDelay(peekTile, unpeekTile, 300);
and to stop it:
clearInterval(si);
This will just cycle through the functions calling the next one in the list every delay msec, repeating back at the beginning when the last one is called. This will result in your peekTile, wait, unpeekTile, wait, peekTile, etc.
If you prefer to start/stop at will, perhaps a more generic solution would suit you:
function Cycler(f) {
if (!(this instanceof Cycler)) {
// Force new
return new Cycler(arguments);
}
// Unbox args
if (f instanceof Function) {
this.fns = Array.prototype.slice.call(arguments);
} else if (f && f.length) {
this.fns = Array.prototype.slice.call(f);
} else {
throw new Error('Invalid arguments supplied to Cycler constructor.');
}
this.pos = 0;
}
Cycler.prototype.start = function (interval) {
var that = this;
interval = interval || 1000;
this.intervalId = setInterval(function () {
that.fns[that.pos++]();
that.pos %= that.fns.length;
}, interval);
}
Cycler.prototype.stop = function () {
if (null !== this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
Example usage:
var c = Cycler(peekTile, unpeekTile);
c.start();
// Future
c.stop();
You use setInterval() to call unpeekTile() every 1000 milliseconds and then you call setTimeOut() to run peekTile() after 1000 milliseconds at the end of the unpeekTile() function:
function peekTile() {
var peekAnimation = WinJS.UI.Animation.createPeekAnimation([tile1, tile2]);
// Reposition tiles to their desired post-animation position
tile1.style.top = "-150px";
tile2.style.top = "-150px";
peekAnimation.execute();
}
function unpeekTile() {
/* your code here */
setTimeout(peekTile, 1000);
}
setInterval(unpeekTile, 1000);
Check out the fiddle
var animation = (function () {
var peekInterval, unpeekInterval, delay;
return {
start: function (ip) {
delay = ip;
peekInterval = setTimeout(animation.peekTile, delay);
},
peekTile: function () {
//Your Code goes here
console.log('peek');
unpeekInterval = setTimeout(animation.unpeekTile, delay);
},
unpeekTile: function () {
//Your Code goes here
console.log('unpeek');
peekInterval = setTimeout(animation.peekTile, delay);
},
stop: function () {
clearTimeout(peekInterval);
clearTimeout(unpeekInterval);
}
}
})();
animation.start(1000);
// To stop
setTimeout(animation.stop, 3000);
​
I can't use this instead of animation.peekTile as setTimeout executes in global scope

How to detect a long touch pressure with javascript for android and iphone?

How to detect a long touch pressure with javascript for android and iphone?
native javascript or jquery...
I want something that sound like :
<input type='button' onLongTouch='myFunc();' />
The problem with using Touch End to detect the long touch is it won't work if you want the event to fire after a certain period of time. It is better to use a timer on touch start and clear the event timer on touch end. The following pattern can be used:
var onlongtouch;
var timer;
var touchduration = 500; //length of time we want the user to touch before we do something
touchstart() {
timer = setTimeout(onlongtouch, touchduration);
}
touchend() {
//stops short touches from firing the event
if (timer)
clearTimeout(timer); // clearTimeout, not cleartimeout..
}
onlongtouch = function() { //do something };
Here is an extended version of Joshua answer, as his code works well till user doesn't perform multitouch (you can tap screen with two fingers and function will be triggered two times, 4 fingers - 4 times).
After some additional test scenarios I even triggered possibility to touch very freequently and receive function executing after each tap.
I added variable named 'lockTimer' which should lock any additional touchstarts before user trigger 'touchend'.
var onlongtouch;
var timer;
var touchduration = 800; //length of time we want the user to touch before we do something
function touchstart(e) {
e.preventDefault();
if (!timer) {
timer = setTimeout(onlongtouch, touchduration);
}
}
function touchend() {
//stops short touches from firing the event
if (timer) {
clearTimeout(timer);
timer = null;
}
}
onlongtouch = function() {
timer = null;
document.getElementById('ping').innerText+='ping\n';
};
document.addEventListener("DOMContentLoaded", function(event) {
window.addEventListener("touchstart", touchstart, false);
window.addEventListener("touchend", touchend, false);
});
<div id="ping"></div>
The solutions posted here ignore the fact that the user needs to touch the screen to initiate scroll. We only want the long-press behavior if the user is not trying to scroll.
function onLongPress(element, callback) {
let timer;
element.addEventListener('touchstart', () => {
timer = setTimeout(() => {
timer = null;
callback();
}, 500);
});
function cancel() {
clearTimeout(timer);
}
element.addEventListener('touchend', cancel);
element.addEventListener('touchmove', cancel);
}
And then:
onLongPress(element, () => {
console.log('Long pressed', element);
});
I've done it this way in my Android app:
registered events listeners:
var touchStartTimeStamp = 0;
var touchEndTimeStamp = 0;
window.addEventListener('touchstart', onTouchStart,false);
window.addEventListener('touchend', onTouchEnd,false);
added functions:
var timer;
function onTouchStart(e) {
touchStartTimeStamp = e.timeStamp;
}
function onTouchEnd(e) {
touchEndTimeStamp = e.timeStamp;
console.log(touchEndTimeStamp - touchStartTimeStamp);// in miliseconds
}
checked time difference and did my stuff
I hope this will help.
Building on the solution by #djanowski to handle touch scroll. This should also prevent context menu and selection on long press.
function onLongPress(element, callback) {
var timeoutId;
element.addEventListener('touchstart', function(e) {
timeoutId = setTimeout(function() {
timeoutId = null;
e.stopPropagation();
callback(e.target);
}, 500);
});
element.addEventListener('contextmenu', function(e) {
e.preventDefault();
});
element.addEventListener('touchend', function () {
if (timeoutId) clearTimeout(timeoutId);
});
element.addEventListener('touchmove', function () {
if (timeoutId) clearTimeout(timeoutId);
});
}
onLongPress(document.getElementById('kitty1'), function(element) {
alert('Meow from ' + element.outerHTML );
});
onLongPress(document.getElementById('kitty2'), function(element) {
alert('Meow from ' + element.outerHTML );
});
img {
max-width: 100%;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
user-select: none; /* Standard syntax */
}
<p>Long press on kitty! Kitty should meow on 500ms long press but not scroll</p>
<img id="kitty1" src="http://placekitten.com/300/400" />
<img id="kitty2" src="http://placekitten.com/300/300" />
We can calculate the time difference when the touch started and when the touch end. If the calculated time difference exceed the touch duration then we use a function name taphold.
var touchduration = 300;
var timerInterval;
function timer(interval) {
interval--;
if (interval >= 0) {
timerInterval = setTimeout(function() {
timer(interval);
});
} else {
taphold();
}
}
function touchstart() {
timer(touchduration);
}
function touchend() {
clearTimeout(timerInterval);
}
function taphold(){
alert("taphold");
}
document.getElementById("xyz").addEventListener('touchstart',touchstart);
document.getElementById("xyz").addEventListener('touchend',touchend);
For cross platform developers:
Mouseup/down seemed to work okay on android - but not all devices ie (samsung tab4). Did not work at all on iOS.
Further research its seems that this is due to the element having selection and the native magnification interupts the listener.
This event listener enables a thumbnail image to be opened in a bootstrap modal, if the user holds the image for 500ms.
It uses a responsive image class therefore showing a larger version of the image.
This piece of code has been fully tested upon (iPad/Tab4/TabA/Galaxy4):
var pressTimer;
$(".thumbnail").on('touchend', function (e) {
clearTimeout(pressTimer);
}).on('touchstart', function (e) {
var target = $(e.currentTarget);
var imagePath = target.find('img').attr('src');
var title = target.find('.myCaption:visible').first().text();
$('#dds-modal-title').text(title);
$('#dds-modal-img').attr('src', imagePath);
// Set timeout
pressTimer = window.setTimeout(function () {
$('#dds-modal').modal('show');
}, 500)
});
This better solution based on #Joshua, sometimes the code need to be called directly inside event (some web API require user acction to trigger something) for this case you can use this modification:
var longtouch;
var timer;
//length of time we want the user to touch before we do something
var touchduration = 500;
function touchstart() {
longtouch = false;
timer = setTimeout(function() {
longtouch = true;
timer = null
}, touchduration);
}
function touchend() {
if (timer) {
clearTimeout(timer);
timer = null;
}
if (longtouch) {
// your long acction inside event
longtouch = false;
}
}
in setTimeout you set the flag to true and inside touchend, you check if it was set.
This worked for my use-case i.e wanted to execute certain function for the time screen is touched.
let triggerInterval = 200; // in milliseconds
let timerId;
function touchstart(e) {
// e.preventDefault();
timerId = setInterval(yourFunction, triggerInterval);
}
function touchend(e) {
clearInterval(timerId);
}
function yourFunction() {
// perform your logic
}
document.addEventListener("touchstart", touchstart);
document.addEventListener("touchend", touchend);
Note:- Smaller value in triggerInterval will execute yourFunction() more faster.
When you are done with your program, then you can remove the respective event Listeners:
document.removeEventListener("touchstart", touchstart);
document.removeEventListener("touchend", touchend);
Long tap event that working in all browser
(function (a) {
function n(b) { a.each("touchstart touchmove touchend touchcancel".split(/ /), function (d, e) { b.addEventListener(e, function () { a(b).trigger(e) }, false) }); return a(b) } function j(b) { function d() { a(e).data(h, true); b.type = f; jQuery.event.handle.apply(e, o) } if (!a(this).data(g)) { var e = this, o = arguments; a(this).data(h, false).data(g, setTimeout(d, a(this).data(i) || a.longclick.duration)) } } function k() { a(this).data(g, clearTimeout(a(this).data(g)) || null) } function l(b) {
if (a(this).data(h)) return b.stopImmediatePropagation() ||
false
} var p = a.fn.click; a.fn.click = function (b, d) { if (!d) return p.apply(this, arguments); return a(this).data(i, b || null).bind(f, d) }; a.fn.longclick = function () { var b = [].splice.call(arguments, 0), d = b.pop(); b = b.pop(); var e = a(this).data(i, b || null); return d ? e.click(b, d) : e.trigger(f) }; a.longclick = { duration: 500 }; a.event.special.longclick = {
setup: function () {
/iphone|ipad|ipod/i.test(navigator.userAgent) ? n(this).bind(q, j).bind([r, s, t].join(" "), k).bind(m, l).css({ WebkitUserSelect: "none" }) : a(this).bind(u, j).bind([v,
w, x, y].join(" "), k).bind(m, l)
}, teardown: function () { a(this).unbind(c) }
}; var f = "longclick", c = "." + f, u = "mousedown" + c, m = "click" + c, v = "mousemove" + c, w = "mouseup" + c, x = "mouseout" + c, y = "contextmenu" + c, q = "touchstart" + c, r = "touchend" + c, s = "touchmove" + c, t = "touchcancel" + c, i = "duration" + c, g = "timer" + c, h = "fired" + c
})(jQuery);
Bind longclick event with time interval
$('element').longclick(250, longClickHandler);
below function fire on Long Tap on touch device
function longClickHandler() {
alter('Long tap Fired');
}

Categories

Resources