When does a click become a hold? - javascript

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.

Related

Prevent javascript throttle function repeating if user pauses on trigger

I want to resize a header when the user scrolls, by adding/subtracting a class. But, I'm missing something. The script below fires repeatedly, re-adding the class, If a user slowly scrolls down the page to the offset point that triggers the function. It doesn't matter what time I set, the user has still scrolled to that location and stopped - triggering the script repeatedly. I tried with a basic debouncer, and that didn't work - I got similar issues.
Here's my script:
let throttlePause;
const throttle = (callback, time) => {
if (throttlePause) return;
throttlePause = true;
setTimeout(() => {
callback();
throttlePause = false;
}, time);
};
function scrollShrink() {
document.getElementById('wrapper').classList.toggle('page-scrolled', window.pageYOffset >= 20);
}
// run through throttler
window.addEventListener("scroll", () => {
throttle(scrollShrink, 200);
});

Click event on a button with SetInterval Method

I'm not able to figure out how this can be accomplished. I'm working on the chrome extension and I have a setInterval method which monitors a button on a page and when its clicked, it runs a function. but the problem is, when I click the button, the function runs multiple times and I clearly understand this is because of the Interval function, but I want the setInterval to run always on this page so that I can monitor if the button is clicked or not. Below is my code
$(function(){
var url=window.location.href;
findAction(url);
}
function findAction(url){
setInterval(()=>{
Acceptbtn = $("[slot=primary-content-area")[4].querySelector("sn-inbox");
if(Acceptbtn !== undefined && Acceptbtn !== null){
Acceptbtn.addEventListener('click', myFunction);
}
function myFunction() {
console.log("clicked");
runAction(url)
};
},1000);
}
Is there any way to tackle this situation or have I made something simple, complicated or Is my approach completely wrong.?
Below is the Div which my extension monitors for the Accept button -
And once the Accept button is clicked, this is what happens
The Interval method keeps checking for this button and once found and I click on it, I want the runAction(url) function to execute. With my current code, the function gets executed but multiple times
Only add an event listener once
Also the selector is incorrect if sn-inbox is a class and [slot=primary-content-area] was missing a ]
Here I delegate - if you delegate to the closest static container (here document, because I do not know the closest container in your case), you can ALWAYS find the button when it is there:
const url = window.location.href;
$(document).on("click", "[slot=primary-content-area]", function() {
if ($(this).index() === 3) { // 0 based - so the 4th slot
const $inbox = $(this).find(".sn-inbox");
if ($inbox.length > 0) {
$inbox.html("running");
runAction(url)
}
}
})
Example code to execute the functions:
https://jsfiddle.net/mplungjan/p93cj0Le/
$(function() {
const runAction = url => console.log("running", url);
const url = window.location.href;
$(document).on("click", "[slot=primary-content-area]", function() {
if ($(this).index() === 3) { // 0 based
const $inbox = $(this).find(".sn-inbox");
if ($inbox.length > 0) {
$inbox.html("running");
runAction(url)
}
}
})
// testing the code
setInterval(() => {
$("#container").html(`<div>
<div slot="primary-content-area"></div>
<div slot="primary-content-area"></div>
<div slot="primary-content-area"></div>
<div slot="primary-content-area"><button class="sn-inbox">Click</button></div>
</div>`)
}, 5000)
setTimeout(() => $("#container").html(""), 2000)
})
<div id="container">
</div>

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();

Javascript: How to ensure that onClick events are only triggered once prior events have completed?

I've written code that colors points on a canvas when the user clicks them. Points are colored by way of a fade-in animation, so it takes a certain amount of time to color a given point. Problems occur if the user clicks a new point while a prior point is still being colored: anomalous colors are produced, fade-in animations continue indefinitely without stopping, etc.
I would like is to make it so that, when the user clicks on a point, any subsequent clicks are ignored until the function coloring that point has completed. Upon completion, the user should be able to click another point to color it (i.e. the onClick coloring functionality should be restored).
In searching for solutions to this problem, I mostly found code designed for events that are only executed once. However, this is not quite what I need. My user should be able to be able to trigger an arbitrary number of coloring events--I just don't want any of these events to ever happen concurrently, as it were. Thanks for any help!
You can use locking, like:
var block = false;
element.addEventListener("click", function () {
if (block === true) {
return;
}
block = true;
doOperation("params", function () {
block = false;
});
});
function doOperation(input, callback) {
setTimeout(callback, 1000);
}
This blocks clicks for about 1 second.
Commonly you'll use promises instead of the callback:
var block_promise = null;
element.addEventListener("click", function () {
if (block_promise === null || block_promise.is_resolved() === false) {
return;
}
block_promise = doOperation("params");
});
function doOperation(input, callback) {
var promise = new Promise();
setTimeout(function () {
promise.resolve();
}, 1000);
return promise.promise();
}
Note that Promises are not (yet) natively supported in JavaScript. Use your favorite library.
If you have a way of setting a flag when painting is occurring, you could try this. Here I set a flag to true when painting starts and false when it is done. In the buttonClick() function, I immediately exit if painting is true.
<!DOCTYPE html>
<html>
<head>
<title>Button Test</title>
</head>
<body>
<script>
var painting = false;
var clicks = 0;
function buttonClick() {
//if painting, immediately exit buttonClick
if (painting)
return;
//display click count whenever we enter here
clicks++;
var obj = document.getElementById("clicks");
obj.value=clicks;
//simulate painting
painting = true;
document.getElementById("status").innerHTML = "painting in progress";
setTimeout(function() { colorPoint()}, 10000 /* 10 seconds */ );
}
function colorPoint() {
//simulated painting
painting = false; //allows button to be clicked again
document.getElementById("status").innerHTML = "painting done";
}
</script>
<button onclick="buttonClick()">Click me</button>
Button clicks: <input type="text" name="clicks" id="clicks">
<div id="status"> </div>
</body>
</html>

Timed long press in Javascript within jQuery phonegap app

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

Categories