JQuery click only registers first click and not subsequent clicks - javascript

I have a button group of three buttons. The relevant haml code is:
.btn-group#refresh-buttons
%button.btn.btn-default#refresh-5-sec 5 sec
%button.btn.btn-default#refresh-30-sec 30 sec
%button.btn.btn-default#refresh-60-sec 60 sec
I'm trying to build an adjustable refresh-rate feature. The first click registers and sets the refresh rate correctly (I inspected the event object) but subsequent clicks on any button thereafter do not register, they do not create an event object. My JS code is below:
$(document).ready(function() {
var intervalId = window.setInterval(function(){
$('.container').load('/dashboard/index .container');
}, 60000);
console.log(intervalId);
$("#refresh-buttons").on("click", "button", function(event) {
if(event.target.id === "refresh-5-sec") {
clearInterval(intervalId);
setInterval(function(){
$('.container').load('/dashboard/index .container');
}, 5000);
}
else if(event.target.id === "refresh-30-sec") {
clearInterval(intervalId);
setInterval(function(){
$('.container').load('/dashboard/index .container');
}, 10000);
}
else if(event.target.id === "refresh-60-sec") {
clearInterval(intervalId);
setInterval(function(){
$('.container').load('/dashboard/index .container');
}, 15000);
}
});
});
Thanks.

As already mentioned, if the .container element contains the button group then you need to use event delegation. There was another issue in the code, however. You were not setting intervalId each time you changed the refresh rate, so you would end up with multiple instances of setInterval() running.
I also tidied up a little as there was some code duplication going on...
$(document).ready(function() {
var intervalId = window.setInterval(function(){
$('.container').load('/dashboard/index .container');
}, 60000);
console.log(intervalId);
$(document).on("click", "#refresh-buttons button", function(event) {
var interval = 0;
switch(event.target.id) {
case: "refresh-5-sec" {
interval = 5000;
break;
}
case: "refresh-30-sec" {
interval = 30000;
break;
}
case: "refresh-60-sec" {
interval = 60000;
break;
}
}
if (interval != 0)
{
clearInterval(intervalId);
intervalId = setInterval(function(){
$('.container').load('/dashboard/index .container');
}, interval);
}
}
});
You should really change the click event handler so it's not attached to document. That example will work no matter how you change your page, but it's advisable to use the closest selector to the buttons as possible, that does not change when you do a load().

As #adeneo points out in his comment above, the #refresh-buttons element can't be inside the .container element, because you overwrite it on load() and therefore remove the event handler you attached.
If the #refresh-buttons element is inside the .container element, you have to use a delegate on a non-changing parent element, like this:
$(".container").on("click", "#refresh-buttons button", function(event) {...});

Related

clearTimeout on mouseup doesnt work right

I want to click on a point and delete it, but when i press my mouse button down for 3 seconds, i want to do something else with this point. My problem is, that the code doesnt clear the timer, it just deletes the point after 3 seconds, no matter how long i clicked it on the point.
Im working with setTimeout and clearTimeout.
function click(event,d){
timer= setTimeout(function(){
/* do something */
},3000)
}
function clickRelease(timer){
clearTimeout(timer)
}
divMag1=d3.selectAll(".scatterlayer .trace .points path ")
divMag1.on("mousedown", click)
divMag1.on("mouseup",clickRelease)```
V3 - I think you're deleting the target before you can execute what you want.
Note that setTimout may take more than 3 seconds to execute.
Try:
function click(event) {
const currentTarget = event.currentTarget; // for some reason event.target is null in the timer handler, this fixes it 🤷
const timer = setTimeout(() => {
currentTarget.removeEventListener('mouseup', deleteTarget);
console.log('stuff');
// do stuff
}, 3000);
function deleteTarget() {
clearTimeout(timer);
console.log('deleted');
currentTarget.removeEventListener('mouseup', deleteTarget); // not required if you're actually deleting the target
// remove target
}
currentTarget.addEventListener('mouseup', deleteTarget);
}
document.querySelector('#but').addEventListener('mousedown', click)
<button id="but">Click</button>
V1:
let timer;
function click(event,d){
timer= setTimeout(function(){
/* do something */
},3000);
}
function clickRelease(){
clearTimeout(timer)
}
divMag1=d3.selectAll(".scatterlayer .trace .points path ")
divMag1.on("mousedown", click)
divMag1.on("mouseup",clickRelease)
You should first declare timer variable. Then to make your mouseup event works on you should wrap clickRelease to event funtion again or use it simply:
...
.addEventListener("mouseup", function() { clearTimeout(timer) })
Working example with button:
var timer
function click(event, d) {
timer = setTimeout(function () {
console.log('do something')
}, 1000)
}
function clickRelease(timer){
clearTimeout(timer)
}
var divMag1 = document.querySelector('#but')
divMag1.addEventListener("mousedown", click)
document.addEventListener("mouseup", function() { clickRelease(timer) })
<button id="but">Click</button>
If you want event to doing something repeatedly while button is down you need to use setInterval not setTimeout like this:
var timer
function click(event, d) {
timer = setInterval(function () {
console.log('do something')
}, 1000)
}
var divMag1 = document.querySelector('#but')
divMag1.addEventListener("mousedown", click)
document.addEventListener("mouseup", function() { clearInterval(timer) })
Note for clearing interval uses clearInterval not clearTimeout. Also the mouseup event handler attached on whole document in my solution not on button.

On / Off function after a certain period of time

Wondering how you can make .off() only happen for a specific period of time. I have a div which once clicked i want to disable the click event for 2 seconds, and then be allowed to click again. At the moment all I have is the div can be clicked, then once clicked it is off.
Here is a brief example of what I am asking:
$('.test').on('click', function() {
// *do stuff*
$('.test').off('click'); *for a certain perdiod of time*
});
It's a much simpler task to use a boolean variable as a flag to state whether the click handler should be executed, instead of attaching/detaching events from multiple elements. Try this:
var clickEnabled = true;
$('div').click(function() {
clickEnabled = false;
setTimeout(function() {
clickEnabled = true;
}, 2000);
});
$('.test').on('click', function(e) {
if (!clickEnabled) {
e.preventDefault();
} else {
// do stuff...
}
});
Note that you should also make the fact that .test is disabled visible in the UI, otherwise you'll just confuse and annoy your visitors when they click an element expecting an action, but nothing happens.
What about activate on after a certain period of time?
function myFunction() {
...do stuff
$('.test').off('click'); for a certain perdiod of time
setTimeout(function(){ $('.test').on('click', myFunction)}, 2000);
}
Using the setTimeout you may do something like:
var enabled = true;
var timeoutSeconds = 2;
$(function () {
$('.test').on('click', function(e) {
if (enabled) {
// *do stuff*
enabled = false;
window.setTimeout(function() {
enabled = true;
}, timeoutSeconds * 1000);
} else {
e.preventDefault();
}
});
});
You could do it like this.
function onClick() {
// do stuff here
$('.test').off('click');
setTimeout(function(){
$('.test').on('click', onClick);
}, 2000)
}
$('.test').on('click', onClick);
Or you can do it with css and toggleClass
// css
.disabled {
pointer-events: none;
}
// js
$('.test').on('click', function(){
// do stuff here
$('.test').addClass('disabled');
setTimeout(function(){
$('.test').removeClass('disabled');
}, 2000);
});

How can I depend on the interval that I just cleared in jquery?

It's a follow up to this question - https://stackoverflow.com/a/33430608/3766930
Basically I have a text area and when user starts typing in sth, the counter starts going down from 3 to 0. when it reaches 0 it gets disabled.
Now I want to add a feature of starting over - when user clicks the link start over, text area goes enabled again and user has 3 seconds (again) to perform the input.
I modified the jquery script:
$('#textArea').on('input propertychange', display30Seconds);
var interval;
function display30Seconds() {
var validTime = 3000;
if (!interval)
interval = setInterval(function () {
$('#counter').html(validTime / 1000);
validTime = validTime - 1000;
if (validTime < 0) {
clearInterval(interval);
alert('Time Up!');
$('#textArea').prop('disabled', true);
$('#counter').html('start over');
$('#counterIsDone').on('click', function(){
$('#textArea').prop('disabled', false);
display30Seconds();
});
}
}, 1000);
}
but I see that I cannot call the method display30Seconds(); again. Or rather I can, but the interval is not set again. How can I fix it?
Seems like I'm not entering the code inside
if (!interval)
because the interval is not visible any more after clearing it (?). So I thought about moving the var interval; into the body of the method function display30Seconds() {, but that doesn't bring the expected effect. Is there a way of fixing it?
Here is my updated fiddle: http://jsfiddle.net/jf4ea4nx/3/
Set interval=null after the clearInterval() call.
What seems to confuse you is the semantics of clearInterval(interval). As Patrick Evans points out in his comment, it will not set interval to a value that evaluates to false in a condition.
To make it completely clear you could use a boolean variable such as countdownRunning in addition to the interval variable to keep track of whether the countdown is active or not.
Try this:
$('#textArea').on('input propertychange', display30Seconds);
var interval=false;
function display30Seconds() {
var validTime = 3000;
if (!interval)
interval = setInterval(function () {
$('#counter').html(validTime / 1000);
validTime = validTime - 1000;
if (validTime < 0) {
clearInterval(interval);
alert('Time Up!');
$('#textArea').prop('disabled', true);
$('#counter').html('start over');
$(document).on('click','#counterIsDone', function(){
$('#textArea').prop('disabled', false);
display30Seconds();
});
interval=false;
}
}, 1000);
}
You can improve your code by using a conditional recursive call to to your iterative function instead - each call has a one second delay, which makes it slightly more intuitive to use (think of each call as one tick):
var seconds = 3;
$('#textArea').on('input propertychange', setTimeout(timeout.bind(null, seconds), 1000));
function timeout (iterations) {
$('#counter').html(iterations);
if (iterations === 0) {
alert('Time Up!');
$('#textArea').prop('disabled', true);
$('#counter').html('start over');
$('#counterIsDone').on('click', function(){
$('#textArea').prop('disabled', false);
timeout(seconds);
});
}
else {
setTimeout(timeout.bind(null, --iterations), 1000);
}
}
The bind function simply binds the arguments of the bind function to the arguments of the timeout call - the first argument to the bind function declares its this scope; but don't worry about that too much.
You can modify the duration of the timer by changing the seconds var. Hope this helps :)

jQueryMobile & timers - stop previous timer on ajax page load, variable scope

I have run into a snafoo using jQuery Mobile, backbone, and regular ole javascript timers. The web app has an idle timer that on runs on two sequential pages. The problem is that the way I am currently handling events (wrapping everything in pageshow), my tests reveal that the timer from the first page continues to run in addition to the second timer instantiated on the subsequent pageshow event - resulting in a flicker of the session countdown (two instances of the countdown variable being displayed simultaneously).
I tried checking to see if the idleInterval already existed to no avail (result was always that it didn't exist).
I imagine I could listen for one of the other 'pagechange' events - like one that is triggered at the very start and stop the previous timer - however then I would have to wrap different parts of code inside different 'pagechange' events and thus have scope issues (need to refer to same backbone models in either case). What a mess...
$(document).on('pageinit pageshow', 'div:jqmData(role="page"), div:jqmData(role="dialog")', function (event) {
if($(this).attr("id")!="KioskDefaultLogin" && $(this).hasClass('kiosk-mode') && event.type=='pageshow') {
...backbone js code...
function sessionTimeout() {
var inactivetime = 0;
console.log('TIMER EXISTS?=>'+window.hasOwnProperty('idleInterval'));
if( qnumerItem.get("state") != "login") {
var idleInterval = setInterval(function(){
if(qnumerItem.get("state") != "timeout") {
inactivetime ++;
console.log('TIMER -----'+inactivetime+'------');
}
////console.log("timer 1: "+inactivetime);
/*Edit the inactivetime value here to change the idle session timer*/
if (inactivetime == 15 && qnumerItem.get("state") != "timeout") {
qnumerItem.changeState("timeout");
inactivetime=0;
/*End Session Due to Inactivity Countdown*/
$(function(){
var count=30;
console.log("I STARTED");
var countdown = setInterval(function(){
//$(".ui-page-active .session-countdown").html(count);
//$(".ui-page-active .to-phrase").show();
if (count == 0) {
clearInterval(countdown);
qnumerItem.changeState("kill");
}
count--;
console.log("timer 1: "+inactivetime);
console.log("timer 2: "+count);
}, 1000);
$(this).on( 'click', '.to-continue', function(event) {
event.preventDefault();
clearInterval(countdown);
$("#session-countdown").html(60);
});
$(this).on( 'click', '.to-end', function(event) {
event.preventDefault();
clearInterval(countdown);
});
});
}
}, 1000); // 1 second
}
//Zero the idle timer on mouse movement.
document.addEventListener('touchstart', function(e) {
e.preventDefault();
inactivetime = 0;
}, false);
document.addEventListener('touchmove',
function (e) {
e.preventDefault();
}, false);
$(this).mousemove(function (e) {
inactivetime = 0;
});
$(this).keypress(function (e) {
inactivetime = 0;
});
$(this).click(function (e) {
inactivetime = 0;
});
}
sessionTimeout();
...more code....

On keypress, when stop after X seconds call function

I have a text input and a textarea and I'm passing the value from the input to the textarea. I am trying to do, when you type something in the input and you stop, after 2 seconds show the values to the textarea.
In this example the textarea gets the input's value instantly:
http://jsfiddle.net/DXMG6/
So i want, when you type and stop, after 2 seconds give the value.
How can I achieve this? I tried to use setTimeout but when the 2 seconds pass, then it keeps getting the value instantly. So basically it works for the first 2 seconds.
You have to reset the timer everytime the user presses the key again:
jQuery(function($){
function changeFn(){
alert('Changed');
}
var timer;
$("#string").bind("keyup", function(){
clearTimeout(timer);
timer = setTimeout(changeFn, 2000)
});
});
Once i made this plugin called bindDelay for jQuery:
$.fn.bindDelay = function( eventType, eventData, handler, timer ) {
if ( $.isFunction(eventData) ) {
timer = handler;
handler = eventData;
}
timer = (typeof timer === "number") ? timer : 300;
var timeouts;
$(this).bind(eventType, function(event) {
var that = this;
clearTimeout(timeouts);
timeouts = setTimeout(function() {
handler.call(that, event);
}, timer);
});
};
Used like a normal bind method but the last argument is the delay before firing the handler (in mil sec):
$("input").bindDelay('keyup', function() {
$("textarea").text( $(this).val() );
}, 2000);
See fiddle: http://jsfiddle.net/c82Ye/2/
And you unbind and trigger it like normal:
$("input").unbind("keyup");
$("input").trigger("keyup");
setTimeout returns an ID of the "job". what you have to do is to clearTimeout(id) every type and setTimeout again:
var tID = null;
onclick() {
if (tID !== null) clearTimeout(tID);
tID = setTimeout(function() { /*Do domething*/ }, 2000);
}
What you need to do is set a timeout, and save the resulting timeout id. Then you need to check if the timeout id has been saved at each keypress. If the timeout is set, clear the timeout and reset it. Something like this:
var timeoutId = null;
var myFunc = function() {
timeoutId = null;
// Do stuff
};
var myEventHandler = function() {
if (timeoutId) {
window.clearTimeout(timeoutId);
}
timeoutId = window.setTimeout(myFunc, 2000);
};
...or check the updated fiddle:
http://jsfiddle.net/DXMG6/5/
I've updated your fiddle
This will update the textarea value 2 seconds after you end editing the text.
The relevant part is this: we keep a reference to a timeout, when the keyup event is fired we clear the previous timeout and we start a new timeout, that will fire in 2 seconds.
var timeout = null;
$("#string").on("keyup keypress paste mouseup", function () {
clearTimeout(timeout);
timeout = setTimeout(function() {
// ... your code here
}, 2000);
});
Try something like this. Use setTimeout, but each time a key is pressed, reset the timer and start over...
http://jsfiddle.net/DXMG6/10/
var textTimer=null;
$("#string").on("keyup keypress paste mouseup", function () {
if (textTimer) clearTimeout(textTimer);
textTimer = setTimeout(function(){
var a = $('#string').val();
$('#rdonly').html(a);
}, 2000);
});
$('.btn').click(function() {
$('#rdonly').text('');
$('#string').val('');
});
You just need to modify your code as follows:
var timeoutId = 0;
$("#string").on("keyup keypress paste mouseup", function () {
var a = $('#string').val();
// Cancel existing timeout, if applicable
if (timeoutId > 0) {
window.clearTimeout(timeoutId);
}
// Start a timeout for 2 seconds- this will be cancelled above
// if user continues typing
timeoutId = window.setTimeout(function () {
$('#rdonly').html(a);
}, 2000);
});

Categories

Resources