Implement press and hold on a button - javascript

Thank you for the previous (deleted by moderator) explanation and the simple implementation but it does not work out for me. The 'holdit' function works but it is not is not steady probably because there is an 'onmouseup' in the 'holdit' function too, Even if I disable the onmouseup at the HTML button it's not very steady. Maybe it's better to use an addEventListener- onmousedown-interval function but again I don't know how to implement it in the simplest possible way. Here's the complete function that shows a pressed button and increases the timeSeconds var by one. For safety the var. number is within a limit.
Please help.
HTML:
<img id="but4" class="button" src= "//:0" onmousedown="timesecPlus();"onmouseup="timesecPlsUp();"/>
JAVASCRIPT:
function timesecPlus() {
var pmknop = document.getElementById('but5');
pmknop.src = secminBtndwn; //inline Base64 data: button image down (pressed)
timeSeconds = ((timeSeconds>wedstrijdperiode.seconden-6)?(timeSeconds):(++timeSeconds)); //You can ++ chase-back the timeseconds until 5 sec's from period start-time
displayTime( timeSeconds );
};
function timesecPlsUp() {
var pmknop = document.getElementById('but5');
pmknop.src = secminBtn; //inline Base64 data: button image up (normal)
};
// Things I tried:
//holdit(pmknop, function () { ++timeSeconds ; displayTime( timeSeconds );}, 2000, 2);
//pmknop = pmknop.addEventListener('mousedown', function() { interval = setInterval(timesecPlus (), 2000); });
function holdit(btn, action, start, speedup) {
var t;
var repeat = function () {
action();
t = setTimeout(repeat, start);
start = start / speedup;
}
btn.onmousedown = function() {
repeat();
}
btn.onmouseup = function () {
clearTimeout(t);
}
};

The holdit function is taking four variables. The first: btn, is the button id. This is used to determine the action performed whenever the mouse is clicked.
The second variable is a reference to a function. Its called a callback function, since you will be passing a function that will be Caaalleed whenever you call holdit.
The last two variables simply determine when and how long to delay the execution of the repetition and and by how much each repetition will speed up by.
var repeat = function () {
action();
t = setTimeout(repeat, start);
start = start / speedup;
}
Repeat is a recursive function that will be called after 'start' number of milliseconds and be repeated more frequently after each iteration.

Simple implementation:
var btn = document.getElementsByClassName('button')[0];
holdit(btn, function () { timeSeconds++ ; displayTime( timeSeconds );}, 1000, 2);
Implementation without holdit:
var btn = document.getElementsByClassName('button')[0];
var couterFunc, couter=0;
btn.addEventListener('mousedown',function(){couterFunc = setInterval(update,1000); update()})
btn.addEventListener('mouseup',function(){clearInterval(couterFunc)})
/* function that will fire when button press*/
function update(){console.log(++couter)};

Related

global variables in js, how to avoid them

I have a change() function that uses a setInterval() to repeat an animation:
function change(){
interval = setInterval(c,300);
function c(){...}
c()
}
The c() function does the work.
I also have a stop() function that stops all the animation and restore the initial situation:
function stop(){
clearInterval(interval);
...
};
I've read that is better use the var keyword instead declaring a global variable. But I cannot get access from the stop() function to interval if I do so. Declaring interval outside the change() function also gives me a problem.
ok i'm trying the last solution proposed by Ben Aston. Here the code:
function Animator() {
var id;
return {
start: start,
stop: stop,
};
function start() {
id = requestAnimationFrame(go);
}
function stop() {
clearAnimationFrame(id);
}
function go() {
// increment the animation
for (var i=0;i<img.length;i++){
var num = randNum(0,img.length-1)
var btn = img[i].nextSibling.nextSibling.childNodes[1]
img[i].setAttribute("src",img_path[num].path);
$(btn).css({"background-color":img_path[num].color,"border":"4px solid"+img_path[num].color});
$(img[i]).animate({width: "-=80px", height: "-=80px"},'slow');
$(img[i]).animate({width: "+=80px", height: "+=80px"},'slow')}
id = requestAnimationFrame(go)
}
}
basically when the user press a button, the images start to change their width and height and their color.
this is the rest:
var animator = new Animator();
function stop(){ //fn related to the stop button
animator.stop()};
function change(){ //fn related to the start button
animator.start()}
I dont know how to use requestAnimationFrame properly, i'm studying it right now. but when i press the start button the images change just one time and then they stopped.
In the previous code i had a for loop that did the work:
function change(){
interval = setInterval(c,300);
function c(){
for (var i=0;i<img.length;i++){
var num = randNum(0,img.length-1)
var btn = img[i].nextSibling.nextSibling.childNodes[1]
img[i].setAttribute("src",img_path[num].path);
$(btn).css({"background-color":img_path[num].color,"border":"4px solid"+img_path[num].color});
$(img[i]).animate({width: "-=80px", height: "-=80px"},'slow');
$(img[i]).animate({width: "+=80px", height: "+=80px"},'slow')}}
c()}
I admit that i dont have quite clear how to implement the go func?
thanks
edit: now it works (i was working with another file :)) but i have problem with the stop button
You can use Closure as well, read the documentation here :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
With Closure you can define private and public function.
In raw JavaScript you could do the following:
Using ES5:
function Animator() {
var id;
return {
start: start,
stop: stop,
};
function start() {
id = requestAnimationFrame(go);
}
function stop() {
clearAnimationFrame(id);
}
function go() {
// increment the animation...
if(shouldContinue()) { // you define `shouldContinue`
id = requestAnimationFrame(go);
}
}
}
var animator = new Animator();
animator.start();
// ...
animator.stop()
hi using closures and with set interval things works properly:
var Button_Obj = function(){
var intervalID;
function start(){
return intervalID = setInterval(change,300) //change is the function that do the animation with a loop, i keep it private
};
function stop(){
clearInterval(intervalID)
};
return {start : start, stop : stop}
now in the global space i have only:
var btn_trigger = new Button_Obj();
and in the button tag i put:
<button style="border:1px solid #9dbbe4;" class="change-button" id="change-order" onclick="btn_trigger.start()" type="button">Start Moving</button>
now the global space is more clean, this implmentation works, with RequestAnimationFrame i have some problem, but i will try with it as well later

Ignore function if occurred within x seconds

Since people are misunderstanding my wording, I will rewrite it, I want "with the following code below" to ignore the function which i have commented on below in my jquery if it happened in the last "X" seconds.
Here is my code.
EDIT:: Please write answers in reference to this, example. "the script ignores the change in class and the delay wont work" http://www.w3schools.com/code/tryit.asp?filename=FBC4LK96GO6H
Sorry for confusing everyone including myself.
Edited due to author's post update.
You can create custon event. By this function you will define: "delayedClick" event on the selected objects.
function delayedClickable(selector, delayTime){
$(document).ready(function(){
$(selector).each(function () {
var lastTimeFired = 0;
$(this).click(function(){
if(Date.now() - delayTime > lastTimeFired) {
lastTimeFired = Date.now();
$(this).trigger('delayedClick');
}
});
});
});
}
Remeber that you should define delayTime and this event on selected elements by:
var delayTime = 3 * 1000; // 3 sec delay between firing action
delayedClickable('.Img2', delayTime);
And then just use your event on elements. For example click event can be used in that way:
$element.on('click', function () {
// ...
});
And your custom delayedClick event should be used in that way:
$element.on('delayedEvent', function () {
// ...
});
Full example:
http://www.w3schools.com/code/tryit.asp?filename=FBC56VJ9JCA5
#UPDATE
I've found some another tricky way to keep using click function and makes it works as expected:
function delayedClickable(selector, delayTime){
$(document).ready(function(){
$(selector).each(function () {
var scope = this;
$(this).click(function(){
scope.style.pointerEvents = 'none';
setTimeout(function () {
scope.style.pointerEvents = 'auto';
}, delayTime);
});
});
});
}
And then
var delayTime = 3 * 1000; // 3 sec delay between firing action
delayedClickable('.Img2', delayTime);
That's all.
The key of second way is that we are disabling any pointer event on element when clicked and then after timeout we're turning these events back to work.
https://developer.mozilla.org/en/docs/Web/CSS/pointer-events
And full example:
http://www.w3schools.com/code/tryit.asp?filename=FBC678H21H5F
Can use setTimeout() to change a flag variable and a conditional to check flag in the event handler
var allowClick = true,
delaySeconds = 5;
$(".element1").click(function(){
if(!allowClick){
return; // do nothing and don't proceed
}
allowClick = false;
setTimeout(function(){
allowClick = true;
}, delaySeconds * 1000 );
// other element operations
})

Clearing setInterval() Issue

I have a setInterval on a function X that runs every 500ms. In this function X, I call another function Y that essentially binds an event on some divs. However, I would like to unbind these events the next time the function X is called (to start "fresh"). My code doesn't seem to work:
setInterval(this.board.updateBoard, 500); //called from another constructor
This then initiates the functions below:
Board.prototype.updateBoard = function() {
//I attempt to unbind ALL my divs
var divs = this.$el.find("div");
for(var i = 0; i < divs.length; i++) {
$(divs[i]).unbind(); //Apparently this doesn't work?
}
//...some code here...
//find appropriate $div's (multiple of them), and then calls this.beginWalking() below on each of those
//loop here
this.beginWalking($div, direction + "0", direction + "1");
//end of loop
}
//alternate between classes to give appearance of walking
Board.prototype.beginWalking = function ($div, dir0, dir1) {
return setInterval(function () {
if ($div.hasClass(dir0)) {
$div.removeClass(dir0);
$div.addClass(dir1);
} else {
$div.removeClass(dir1);
$div.addClass(dir0);
}
}.bind(this), 80);
};
Basically, updateBoard is called every 500ms. Each time it's called, beginWalking is called to set another interval on a div. The purpose of this other interval, which functions correctly, is to add and remove a class every 80ms. I just can't seem to unbind everything before the next updateBoard is called.
Any suggestions appreciated!
use clearInterval()
edit: $(selector).toggleClass(dir0) might also be helpful
// In other file, use a global (no var) if you need to read it from another file:
updaterGlobal = setInterval(this.board.updateBoard, 500);
// store interval references for clearing:
var updaterLocals = [];
Board.prototype.updateBoard = function() {
//I attempt to unbind ALL my divs
var divs = this.$el.find("div");
// Stop existing div timers:
while(updaterLocals.length > 0){
clearInterval(updaterLocals[0]);
updaterLocals.shift(); // remove the first timer
}
//...some code here...
//loop here to call the below on several $div's
this.beginWalking($div, direction + "0", direction + "1");
//end of loop
}
//alternate between classes to give appearance of walking
Board.prototype.beginWalking = function ($div, dir0, dir1) {
var interval = setInterval(function () {
if ($div.hasClass(dir0)) {
$div.removeClass(dir0);
$div.addClass(dir1);
} else {
$div.removeClass(dir1);
$div.addClass(dir0);
}
}.bind(this), 80);
// Save the timer:
updaterLocals.push(interval);
return;
};

Fire function within jquery plugin?

I have dynamically created increment and decrement buttons for certain <input> html elements (specifically for numeric inputs), however I cannot for the life of me how to figure out how to bind an action through the plugin function.
I want to do it specifically this way as these arrows will be used on many different types of forms that minus and add values (e.g. %, money etc.) which affect certain visual elements.
The function looks around the lines of:
(function ($) {
$.fn.addIncrementArrows = function (min, max, interval, fastInterval, event) {
var input = this;
/*Other building things go here*/
var timeoutDownArrow;
var intervalDownArrow;
//clicking button down for 1 second fires '-' actions
$(this).parent().children(".downarrow").mousedown(function () {
timeoutDownArrow = setTimeout(function () {
intervalDownArrow = setInterval(function () {
changeValue(-fastInterval);
//how to do event here?
}, 90);
}, 750);
}).bind("mouseup mouseleave", function () {
clearTimeout(timeoutDownArrow);
clearInterval(intervalDownArrow);
});
};
}(jQuery));
Simply what I need to declare to make it have the arrows as follows:
formnamehere.addIncrementArrows(0, 100, 1, 5, event);
If it's possible: how could I bind an action to the button within .addIncrementArrows()?
So after a bit of research I came to a conclusion:
1 - Changed the function to run inside .addIncrementArrows() as a named run-time function:
var setSliderPercentage = function (percent) {
//do stuff here
}
2 - Pass the function into the plugin as a variable name
percentageForm.addIncrementArrows(0, 100, 1, 5, setSliderPercentage);
3 - Ran the passed function within the addIncrementArrows() plugin.
$.fn.addIncrementArrows = function (min, max, interval, fastInterval, event) {
var input = this;
if (max === false) max = Infinity; //infinity and beyond!
//Other stuff here
//make the it auto increment or decrement every 100ms when 'holding down' the button after 1 second
var timeoutUpArrow;
var intervalUpArrow;
$(uparrow).mousedown(function () {
timeoutUpArrow = setTimeout(function () {
intervalUpArrow = setInterval(function () {
changeValue(fastInterval);
//
if (typeof event != "undefined") event(input.val());
//
}, 90);
}, 750);
}).bind("mouseup mouseleave", function () {
clearTimeout(timeoutUpArrow);
clearInterval(intervalUpArrow);
});
}

setTimeout / clearTimeout problems

I try to make a page to go to the startpage after eg. 10sec of inactivity (user not clicking anywhere). I use jQuery for the rest but the set/clear in my test function are pure javascript.
In my frustation I ended up with something like this function that I hoped I could call on any click on the page. The timer starts fine, but is not reset on a click. If the function is called 5 times within the first 10 seconds, then 5 alerts will apear... no clearTimeout...
function endAndStartTimer() {
window.clearTimeout(timer);
var timer;
//var millisecBeforeRedirect = 10000;
timer = window.setTimeout(function(){alert('Hello!');},10000);
}
Any one got some lines of code that will do the trick?
- on any click stop, reset and start the timer.
- When timer hits eg. 10sec do something.
You need to declare timer outside the function. Otherwise, you get a brand new variable on each function invocation.
var timer;
function endAndStartTimer() {
window.clearTimeout(timer);
//var millisecBeforeRedirect = 10000;
timer = window.setTimeout(function(){alert('Hello!');},10000);
}
The problem is that the timer variable is local, and its value is lost after each function call.
You need to persist it, you can put it outside the function, or if you don't want to expose the variable as global, you can store it in a closure, e.g.:
var endAndStartTimer = (function () {
var timer; // variable persisted here
return function () {
window.clearTimeout(timer);
//var millisecBeforeRedirect = 10000;
timer = window.setTimeout(function(){alert('Hello!');},10000);
};
})();
That's because timer is a local variable to your function.
Try creating it outside of the function.
A way to use this in react:
class Timeout extends Component {
constructor(props){
super(props)
this.state = {
timeout: null
}
}
userTimeout(){
const { timeout } = this.state;
clearTimeout(timeout);
this.setState({
timeout: setTimeout(() => {this.callAPI()}, 250)
})
}
}
Helpful if you'd like to only call an API after the user has stopped typing for instance. The userTimeout function could be bound via onKeyUp to an input.
Not sure if this violates some good practice coding rule but I usually come out with this one:
if(typeof __t == 'undefined')
__t = 0;
clearTimeout(__t);
__t = setTimeout(callback, 1000);
This prevent the need to declare the timer out of the function.
EDIT: this also don't declare a new variable at each invocation, but always recycle the same.
Hope this helps.
Practical example Using Jquery for a dropdown menu !
On mouse over on #IconLoggedinUxExternal shows div#ExternalMenuLogin and set time out to hide the div#ExternalMenuLogin
On mouse over on div#ExternalMenuLogin it cancels the timeout.
On mouse out on div#ExternalMenuLogin it sets the timeout.
The point here is always to invoke clearTimeout before set the timeout, as so, avoiding double calls
var ExternalMenuLoginTO;
$('#IconLoggedinUxExternal').on('mouseover mouseenter', function () {
clearTimeout( ExternalMenuLoginTO )
$("#ExternalMenuLogin").show()
});
$('#IconLoggedinUxExternal').on('mouseleave mouseout', function () {
clearTimeout( ExternalMenuLoginTO )
ExternalMenuLoginTO = setTimeout(
function () {
$("#ExternalMenuLogin").hide()
}
,1000
);
$("#ExternalMenuLogin").show()
});
$('#ExternalMenuLogin').on('mouseover mouseenter', function () {
clearTimeout( ExternalMenuLoginTO )
});
$('#ExternalMenuLogin').on('mouseleave mouseout', function () {
clearTimeout( ExternalMenuLoginTO )
ExternalMenuLoginTO = setTimeout(
function () {
$("#ExternalMenuLogin").hide()
}
,500
);
});
This works well. It's a manager I've made to handle hold events. Has events for hold, and for when you let go.
function onUserHold(element, func, hold, clearfunc) {
//var holdTime = 0;
var holdTimeout;
element.addEventListener('mousedown', function(e) {
holdTimeout = setTimeout(function() {
func();
clearTimeout(holdTimeout);
holdTime = 0;
}, hold);
//alert('UU');
});
element.addEventListener('mouseup', clearTime);
element.addEventListener('mouseout', clearTime);
function clearTime() {
clearTimeout(holdTimeout);
holdTime = 0;
if(clearfunc) {
clearfunc();
}
}
}
The element parameter is the one which you hold. The func parameter fires when it holds for a number of milliseconds specified by the parameter hold. The clearfunc param is optional and if it is given, it will get fired if the user lets go or leaves the element. You can also do some work-arounds to get the features you want. Enjoy! :)
<!DOCTYPE html>
<html>
<body>
<h2>EJEMPLO CONOMETRO CANCELABLE</h2>
<button onclick="inicioStart()">INICIO</button>
<input type="text" id="demostracion">
<button onclick="finStop()">FIN</button>
<script>
let cuenta = 0;
let temporalTiempo;
let statusTime = false;
function cronometro() {
document.getElementById("demostracion").value = cuenta;
cuenta++;
temporalTiempo = setTimeout(cronometro, 500);
}
function inicioStart() {
if (!Boolean(statusTime)) {
statusTime = true;
cronometro();
}
}
function finStop() {
clearTimeout(temporalTiempo);
statusTime = false;
}
</script>
</body>
</html>

Categories

Resources