I would like to make something that comes up after user right-clicked many times (5 clicks, for example). Then a div should be shown.
Right now I'm using this code:
document.oncontextmenu = function() {
$("#red").fadeIn(500).show().delay(3000).fadeOut(800);
return false;
};
This works fine, but it misess five attempts like I have said in my request.
UPDATE: Here the right code!
document.oncontextmenu = (function() {
var counter = 0;
return function() {
counter++;
if (counter == 5) {
$("#red").fadeIn(500).show().delay(3000).fadeOut(800, function() {
counter = 0;
});
}
return false;
};
})();
Thanks to #James Thorpe
Use what you have, but wrap it in a closure that has a variable to count how many times the inner function has been executed:
document.oncontextmenu = (function() {
var counter = 0;
return function() {
counter++;
if (counter == 5)
$("#red").fadeIn(500).show().delay(3000).fadeOut(800);
return false;
};
})();
Note that the outer function is invoked immediately to create the variable, then returns the inner function as the event handler. By using such a closure, we keep everything related to the event (including the counter variable) within the local scope without leaking anything to the global scope.
If you want to be able to invoke the div multiple times, you just need to reset the counter each time it's shown. The best place to do that is in the callback function supplied to the fadeOut function, so that the count only begins again once the div has been hidden.
if (counter == 5) {
$("#red").fadeIn(500).show().delay(3000).fadeOut(800, function() {
counter = 0;
});
}
Related
I'm trying to initialize a bunch of jquery functions using a foor loop. I use the for loop's counter in each iteration to call the function on a row in a table.
But it doesn't works. I think it's because the counter doesn't exists within the function. This is because I have already tried to run my function hard-coding the places where the counter should be, with, say, 0, and my function works as intended on row 0 of my table. When I leave the counter in place it throws me an error, says that the html elements I get by Id are null.
Is there something I'm missing? How do I pass the counter into a function in javascript?
Here's my code:
var typingTimer; //timer identifier
var doneTypingInterval = 400; //time in ms (5 seconds)
var i
for (i = 0; i < 10; i++) {
//on keyup, start the countdown
$('#precios-'.concat(i,'-precio')).keyup(function(i){
clearTimeout(typingTimer);
if (document.getElementById('precios-'.concat(i,'-precio')).value) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing," do something
function doneTyping(i) {
var it = i
$.getJSON('./_revisarCaptura', {
unidades: $($($('#precios-'.concat(it,'-precio')).parents()[1]).siblings()[6]).html(),
ultimoPrecio: $($($('#precios-'.concat(it,'-precio')).parents()[1]).siblings()[7]).html().substring(1),
nuevoPrecio: document.getElementById('precios-'.concat(it,'-precio')).value,
nuevasUnidades: document.getElementById('precios-'.concat(it,'-precio')).value,
}, function(data) {
$('#precios-'.concat(it,'-precio')).css('backgroundColor', data.result)
})
return false
}
i++
}
Your code has two problems:
keyup and doneTyping() functions declare i as a parameter. They shadow the i variable outside the loop. The keyup funciton receives the keyup event as i instead.
i is not captured for each iteration of the loop. By the time the callbacks are called, i will be set to 10 instead of the loop counter.
The following code fixes the problem.
var typingTimer; //timer identifier
var doneTypingInterval = 400; //time in ms (5 seconds)
// This i is changed by the loop.
// By the time doneTyping() is called, i will be 10.
var i;
for (i = 0; i < 10; i++) {
// let/const keeps the variable scoped within the loop
const id = `precios-${i}-precio`;
const selector = `#${id}`;
// This is also known as debounce
$(selector).keyup(function(/* remove i */){
clearTimeout(typingTimer);
if (document.getElementById(id).value) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
function doneTyping(/* remove i */) {
$.getJSON('./_revisarCaptura', {
unidades: $($($(selector).parents()[1]).siblings()[6]).html(),
ultimoPrecio: $($($(selector).parents()[1]).siblings()[7]).html().substring(1),
nuevoPrecio: document.getElementById(id).value,
nuevasUnidades: document.getElementById(id).value,
}, function(data) {
$(selector).css('backgroundColor', data.result)
});
return false; // Do you need this?
}
}
See also let vs. var.
My question is: is there a unique way to reset timers without knowing their ID in Javascript? I tried using this code:
var maxId = setTimeout(function(){}, 0);
for(var i=0; i < maxId; i+=1) {
clearTimeout(i);
}
I'm aware this would search for any timer and clear any timer, but nothing happens and I think the console even gives me an error when I use it as a bookmarklet. Also, the value of the timer is stored in innerHTML, as such:
<div id="timer">
<div id="timer-minutes" class="timer-fields">00:00</div>
</div>
A unique way to look at this problem is to override the default functionality of setTimeout. By augmenting the function you can then tap into all uses of setTimeout on the page. Then you can simply create a new method to clear all timeouts.
In the code below, I hot-wire the built-in function (setTimeout) with my own version. As long as this code executes before other scripts on the page, I then can keep track of all invocations of the function on the page. Perhaps you could even store the setTimeouts by callee.
(function(win)
{
var _originalSetTimeout = setTimeout; //Store built-in function
var timers = []; //Maintain a collection of timers
win.setTimeout = function(func, delay) //Hijack the built-in function with your own
{
var timer = _originalSetTimeout(func, delay); //Invoke the built in
timers.push(timer); //Store the timer to your private array
};
win.clearAllTimeouts = function() //Create a new function on the window for resetting all timers
{
for(var i=0,length=timers.length;i<length;i++)
{
win.clearTimeout(timers[i]);
}
};
}(window));
Once this function is wired up you then can test with the following code:
var x=0;
function Test()
{
x++;
console.log('test: ' + x);
setTimeout(Test, 1000);
if(x === 5)
{
clearAllTimeouts();
console.log('all timeouts cleared!');
}
}
Test();
The counter will log up to 5, then once it's it the clearAllTimeouts() function is invoked.
See it in action here:
http://jsbin.com/bulayuro/1/edit
I'd like to add a class to a series of spans using setTimeout() such that the class is added in cascading fashion, creating a visual progression rather than having them all set at once. I've tried so many different ways.
Here's a codepen...
http://codepen.io/geirman/pen/nDhpd
The codepen tries to mimic a working example I've found here...
https://developers.google.com/maps/documentation/javascript/examples/marker-animations-iteration
The problem is that I can't seem to delay the addClass successively, so it happens all at once. Here's the current code.
/* The Problem Code
********************/
var span$ = $("span");
var index = 0;
var factor = 500;
function colorSpans(){
for(var i = 0; i < span$.length; i++){
setTimeout(function(){
animate();
}, factor*index);
}
index = 0;
}
function animate(){
span$[index++].className = "lg";
}
colorSpans();
One more thing, I'd love to do this sans jQuery, but will accept a jQuery solution as well.
In your current code, it looks like the very first call to animate will execute immediately, after which index will be 1. But the loop is likely to finish executing before the second timeout-handler is called (i.e., the loop won't take 500ms to execute). So this means that the remaining spans will appear instantaneously since the value of index will not be updated.
Something like this should work:
function colorSpans() {
for(var i = 0; i < span$.length; i++) {
(function(i) {
setTimeout(function() {
animate(i);
}, factor * i);
})(i); //force a new scope so that the "i" passed to animate()
//is the value of "i" at that current iteration and not
//the value of "i" when the loop is done executing.
}
}
function animate(i) {
span$[i].className = "lg";
}
The above code is also preferable because you're not using a global variable to maintain state between colorSpans and animate (i.e., we're not using index).
But you can make additional improvements by using setInterval instead:
var i = 0;
var intervalId = setInterval(function() {
if(i < span$.length) {
animate(i);
i++;
} else {
clearInterval(intervalId);
}
}, 500)
I think this is cleaner than the setTimeout approach.
Check out the updated codepens:
setTimeout approach
setInterval approach
I am doing some long polling (ajax) and I am looping the following portion of code.. There is code being executed above and below. This code is part of an internal messaging system. A certain portion of the page wil blink when a message arrives. If the user checks the message, it will remove the dash_notify from the JSON response, which needs to turn off the blinking. See below:
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
timer = setInterval(blink, 10);
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
}
console.log("initiate_dash");
blinking($(x));
} else if (!data.dash_notify) {
console.log("good");
clearInterval(timer);
}
The following JSON response that gets sent to this code is:
{"current_date_time":"January 8, 2013 - 4:02 pm","dash_notify":"1"}
It understand the initial blink IF the above data gets passed. If the following gets passed:
{"current_date_time":"January 8, 2013 - 4:02 pm"}
Then it throws an error:
Uncaught ReferenceError: timer is not defined
I cannot figure out how to fix the "else" portion working properly. If the code is initiated when the full dash_notify:1 response is sent, it works perfect. The button will blink, then if the user checks the message, it will no longer send dash_notify:1 and the button stops blinking. But if the code is initiated when dash_notify:1 is NOT set, it doesn't know what to do with the ClearInterval.
Basically I need the else portion fixed.
I have tried using different typeOf === undefined snippets, but it doesn't work.
Any help is appreciated.
Thank you!
EDIT:
This is currently working.. Timer is now defined above the statement
if(data.dash_notify == '1'){
var x = '#dash_notif_blink';
console.log("initiate_dash");
blinking($(x));
}else if (typeof timer != "undefined" && timer) {
clearInterval(timer);
}
}
This is working, but sometimes it trys to kill the timer but it doesn't actually do it. This happens every so often.
Looks like it's not working because timer doesn't exist outside your inner blinking function. I'm making an assumption here that you don't have var timer; somewhere outside the blinking function, which is strongly likely given the error you're getting.
Why this is happening:
If I'm right, and you're not declaring timer anywhere else in your code, then var timer is being implicitly added to the beginning of the blinking function:
function blinking(x) {
var timer;
timer = setInterval(blink, 10);
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
}
That makes timer a local variable inside blinking. Since you never pass it out of the closure, it doesn't exist once you're outside that function. So either you need to pull timer into the outer context (option 1) or make it available from inside blinking (option 2).
What to do:
If you want access to timer outside of that closure, you'll have to do one of two things:
1: Declare timer outside of blinking:
var timer = null;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
//etc...
2: Make it the return value of blinking:
var t;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
var timer = setInterval(blink, 10); //note the var keyword for best practice
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
return timer;
}
console.log("initiate_dash");
t = blinking($(x));
} else if (!data.dash_notify) {
console.log("good");
clearInterval(t);
}
Either one of these will work, and is more or less the same in terms of polluting the outer namespace. I prefer Option 2, because I feel like it's easier to work with a local variable until you need to return it.
Edit:
Your comment said the loop runs infinitely, which means you're creating a brand new interval and reassigning the timer variable every time. This is a separate problem from the one I described above. The old interval is still out there, timer just doesn't point to it anymore. So how can clearInterval(timer) clear out all those intervals? It can't, it can only clear the most recent one.
Basically, you've got a whole bunch of timers all trying to make the thing blink at once.
How you deal with this depends on what you're trying to do. The simplest thing would be to keep no more than one interval running at once, which means you have to clear timer every time.
//same as option 1 above except for `clearInterval(timer)` at the
//beginning of `blinking`
var timer = null;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
clearInterval(timer);
timer = setInterval(blink, 10);
If you need multiple timers running, you'll have to keep track of them all in an array or something:
var timers = [];
//...
function blinking(x) {
timers.push(setInterval(blink, 10));
//...
} else if (!data.dash_notify) {
timers.forEach(function(timer) {
clearInterval(timer);
});
}
Not sure what you did wrong with your typeof check since you did not actually show the whole code, but it should look something like this:
if (typeof timer != "undefined" && timer) {
clearInterval(timer);
}
Basically define your variable timer before you enter your checking procedure (loop?):
var timer;
... some code ...
if ( data.dash_notify && data.dash_notify == '1') {
...
} else if (!data.dash_notify) {
clearInterval(timer);
}
You can call clearInterval( whatever ) without any consequences. Even if whatever is null, undefined, string and so on. Just make sure timer exist.
Passing an invalid ID to clearTimeout does not have any effect (and
doesn't throw an exception). (MDN)
You're getting that error because timer is only declared/initialized in the blinking function. In the place where you call clearInterval(timer), timer doesn't exist.
This is now working beautifully.
*Thank you to everyone who helped!*
if(data.dash_notify === '1' && t === null ){
var x = '#dash_notif_blink';
function blinking(x) {
var timer = setInterval(blink, 10); //note the var keyword for best practice
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
return timer;
}
console.log('initiate_dash_alert');
// Data is passed. Parse to see if dash alert should be called. Secondary protection for
// multiple timer creation.
if(t){return;}else{t = blinking($(x));}
}else if (!data.dash_notify){
clearInterval(t);
console.log('clear_dash_alert');
t = null;
}else{
console.log(t);
console.log('no_push_events');
}
I've been playing around with a site, in which I want to continue clicking a button for i amount of times every interval seconds.
My code is:
clickbidBtn1 = function() {
var bidBtn=document.getElementById("BidButton");
var interval = 15000;
for (var i=3; i>=0; i--){
setTimeout(bidBtn.click(1);,i*interval);
};
I've found out that GM executes all i amount of clicks at the same time, not with the intended delay. is there a way to delay the time of click? Say i wanted the function to click the button every 15 second for i amount of times.
I was thinking of giving it some more variables, and adding one variable in the settimeout code part, which only executes # the click, then comparing increased variables with current ones before going to the next settimeout... but haven't thought it through yet... it seems to be a complicated process for a simple process... :( i wll play around with it a bit
Use setInterval() for this.
One way:
var bidClickTimer = 0;
var numBidClicks = 0;
function clickbidBtn1 ()
{
var interval = 15000;
bidClickTimer = setInterval (function() {BidClick (); }, interval);
}
function BidClick ()
{
numBidClicks++;
if (numBidClicks > 3)
{
clearInterval (bidClickTimer);
bidClickTimer = "";
}
else
{
bidBtn.click (1);
}
}
clickbidBtn1 ();
Alternatively, without using global vars:
function clickbidBtn1 ()
{
var interval = 15000;
this.numBidClicks = 0;
this.bidClickTimer = 0;
this.BidClick = function () {
numBidClicks++;
if (numBidClicks > 3)
{
clearInterval (bidClickTimer);
bidClickTimer = "";
}
else
{
bidBtn.click (1);
}
};
this.bidClickTimer = setInterval (function(thisScope) {thisScope.BidClick (); }, interval, this);
}
clickbidBtn1 ();
Just to explain why your code does not work: You are calling the .click method immediately (putting () after a function name calls the function) and actually passing the return value of that function to setTimeout. The for loop is so fast that everything seem to happen at the same time.
You have to pass a function reference to setTimeout, e.g. an anonymous function:
setTimeout(function() {
bidBtn.click(1);
}, i*interval);