Function not defined using setTimeout - javascript

For some reason after I wrapped my js code in an immediately invoked function expression I'm getting progressBar not defined. Wondering why this is the case?
(function(){
"use strict"
var progressBar = function(){
var bar = document.getElementById('pbar'),
status = document.getElementById('status'),
barValue = bar.value;
status.innerHTML = barValue + "%";
bar.value++;
var increment = setTimeout("progressBar()", 50);
if(bar.value == 100){
status.innerHTML = '100% - Straight Besting';
bar.value = '100';
clearTimeout(increment);
}
}
progressBar();
})()

When you pass a string to setTimeout, it is invoked in the context of the global window object.
Change the setTimeout line to:
var increment = setTimeout(progressBar, 50);
E.g.
(function() {
let i = 0;
let myfunction = function() {
console.log(i++);
if (i < 10) setTimeout(myfunction, 100);
};
myfunction();
})()

Related

How to clearTimeout so that I can re use the function?

So far, this(code below) allows me to delay my function that displays a response for 4 seconds after I click a button. Now I would like to know how I would reset this timout so that I can re-click the button for the delay to occur.
js:
var ball = document.getElementById("ball");
function start(){
console.log("start");
ball.classList.toggle("shake");
};
ball.addEventListener("click", start);
function newResponse() {
var question = document.getElementById("question").value;
var response = ["..."];
for (var i = 0; i < question.length; i++) {
var randomResponse = Math.floor(Math.random() * response.length);
document.getElementById('quoteDisplay').innerHTML = response[randomResponse];
}
}
ball.addEventListener("click", newResponse)
Declare the function outside the timeout so you can reference it multiple times. Then, call setTimeout with the function, assign the result to a variable, and if you want to clear the timeout, call clearTimeout on that variable:
function newResponse() {
var question = document.getElementById("question").value;
var response = ["..."];
for (var i = 0; i < question.length; i++) {
var randomResponse = Math.floor(Math.random() * response.length);
document.getElementById('quoteDisplay').innerHTML =
response[randomResponse];
}
}
// Set the timeout once:
let timeout = setTimeout(newResponse,4000);
// Clear it:
clearTimeout(timeout);
// Set the timeout again:
timeout = setTimeout(newResponse,4000);
// etc
$('#test').click(function(){
var timer = setTimeout(function newResponse() {
alert('Click answered...');
clearTimeout(timer);
},4000)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" value="test" id='test'/>
You can do like this
var timer = setTimeout(function newResponse() {
var question = document.getElementById("question").value;
var response = ["..."];
for (var i = 0; i < question.length; i++) {
var randomResponse = Math.floor(Math.random() * response.length);
document.getElementById('quoteDisplay').innerHTML =
response[randomResponse];
}
clearTimeout(timer);
},4000)

How to you make a Javascript object's method call setInterval with an object method as an argument

I am trying to create a generic countdown timer object in Javascript
I have a method (decrementTimer) that reduces the timeLeft on counter by a fixed amount and another method which uses setInterval to call decrementTimer method.
The problem is that the code only runs once not every second. It looks like setInterval isn't putting the decrementTimer function on the queue inside my object.
I have tried making javascript do an Eval by putting the keyword "window" in front of the setIntervalfunction call but this doesn't work.
I can't use a Javascript class because I can't assume that all browsers support ECMAScript 6. I am using IE11.
I have also found solutions that work when you are doing this in a function but no examples of how to make this work in an object.
I would appreciate some help.
<script>
var myTimer = {
timeLeft: 10,
timerJobNumber: null,
decrementTimer: function(){
alert("decrementTimer called. timeLeft = " + this.timeLeft);
this.timeLeft = this.timeLeft - 1;
if(this.timeLeft<0){
alert("done");
}
},
startTimer: function(){
alert("startTimer called");
this.timerJobNumber = window.setInterval(this.decrementTimer(),10);
alert("Interval Job Number = " + this.timerJobNumber);
},
stopTimer: function(){
clearInterval(this.timerJobNumber);
alert(this.timerJobNumber);
alert("job terminated");
},
resetTimer: function(initialTime){
this.TimeLeft = initialTime;
alert("intitialTime="+intitialTime);
},
getTimeLeft: function(){
return this.timeLeft;
}
};
console.log(myTimer.getTimeLeft());
console.log(myTimer.startTimer() );
console.log(myTimer.getTimeLeft());
</script>
I didn't really check all of your code, but this seems to do what you want :
var myTimer = {
timeLeft: 10,
timerJobNumber: null,
decrementTimer: function(){
console.log("decrementTimer called. timeLeft = " + this.timeLeft);
this.timeLeft = this.timeLeft - 1;
if(this.timeLeft<0){
this.stopTimer();
}
},
startTimer: function(){
this.timerJobNumber = window.setInterval(this.decrementTimer.bind(this),1000);
console.log("Interval Job Number = " + this.timerJobNumber);
},
stopTimer: function(){
clearInterval(this.timerJobNumber);
alert(this.timerJobNumber);
},
resetTimer: function(initialTime){
this.TimeLeft = initialTime;
alert("intitialTime="+intitialTime);
},
getTimeLeft: function(){
return this.timeLeft;
}
};
Note that you can easily transform this into a class like function :
var MyTimer = function() {
this.timeLeft = 10;
this.timerJobNumber = null;
};
MyTimer.prototype.decrementTimer = function() {
console.log("decrementTimer called. timeLeft = " + this.timeLeft);
this.timeLeft = this.timeLeft - 1;
if(!this.timeLeft > 0)
this.stopTimer();
};
MyTimer.prototype.startTimer = function() {
this.timerJobNumber = window.setInterval(this.decrementTimer.bind(this),1000);
console.log("Interval Job Number = " + this.timerJobNumber);
};
MyTimer.prototype.stopTimer = function() {
clearInterval(this.timerJobNumber);
alert(this.timerJobNumber);
};
MyTimer.prototype.resetTimer = function(initialTime) {
this.timeLeft = initialTime;
alert("intitialTime="+intitialTime);
};
MyTimer.prototype.getTimeLeft = function() {
return this.timeLeft;
};
//...
var m = new MyTimer();
m.startTimer();
I think you don't bind your decrementTimer() inside the setInterval() function.
startTimer: function(){
alert("startTimer called");
this.timerJobNumber = window.setInterval(this.decrementTimer.bind(this),10);
alert("Interval Job Number = " + this.timerJobNumber);
}
if you are not familiar this topic, then follow this link. I think it will help you.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

javascript interval in Object

i've wrote a snippet which should start counting a number from 1 to 1000 or pre defined.
Because of needing the script mulitple times i thought it would be a great idea to write it as an object and use it mulitiple times. But i get an error:
Uncaught ReferenceError: root is not defined
(anonymous function)
What did i wrong?
var time = function() {
var root = this;
var i=0;
root.max = 1000;
root.elm = false;
root.runTime = function() {
if(root.elm != false) {
if (i < root.max) {
i++;
root.elm.text(i);
} else {
clearInterval(root.interval);
}
}
this.interval = setInterval('root.runtTime()', 5);
};
};
if($(document).ready(function() {
var countUp= new time();
countUp.max = 1526;
countUp.elm = $("#elm");
countUp.runTime();
});
This is because of the following line:
this.interval = setInterval('root.runtTime()', 5);
Because it's a string it has to be evaluated as a global object.
Change to the following to ensure the same scope:
this.interval = setInterval(root.runtTime, 5);
Also there's a typo (runtTime should be runTime), so change to the following:
this.interval = setInterval(root.runTime, 5);
Finally you're using setInterval which will repeatedly call root.runTime every 5ms. Change to setTimeout if you wish to call this recursively:
this.interval = setTimeout(root.runTime, 5);
Alternatively set up the interval outside of your runTime function:
root.runTime = function() {
if(root.elm != false) {
if (i < root.max) {
i++;
root.elm.text(i);
} else {
clearInterval(root.interval);
}
}
};
this.interval = setInterval(root.runTime, 5);
Also you don't need the if statement around document.ready. This is a callback function which is triggered when the DOM has loaded, and therefore doesn't require an if statement.
$(document).ready(function() {
var countUp= new time();
countUp.max = 1526;
countUp.elm = $("#elm");
countUp.runTime();
});

Running a function for each element

I am using the Peity js plugin to create donut charts on my page. I am trying to animate the chart for each of the .foo elements:
<span class="foo" data-value="10"></span>
$('.foo').each(function () {
var updateChart = $(this).peity('donut');
var text = "";
var i = 0;
function myLoop() {
setTimeout(function () {
text = i + "/12";
updateChart.text(text)
.change()
i = i + 0.2;
var maxValue = $(this).data("value");
if (i <= maxValue) myLoop();
}, 0.5)
}
myLoop();
});
However it won't work for some reason with no errors in console. If I remove the $('.foo').each(function () { ... } part (and all "this" instances) the code will work. Thanks in advance for any help.
The problem is the context inside the timer handler, the easiest fix here is to use a closure variable
$('.foo').each(function () {
var $this = $(this);
var updateChart = $this.peity('donut');
var text = "";
var i = 0;
function myLoop() {
setTimeout(function () {
text = i + "/12";
updateChart.text(text)
.change()
i = i + 0.2;
var maxValue = $this.data("value");
if (i <= maxValue) myLoop();
}, 0.5)
}
myLoop();
});
When the timeout callback is executed, the this context refer to window, because you are actually calling window.setTimeout method.
Try this:
$('.foo').each(function () {
var updateChart = $(this).peity('donut');
var text = "";
var i = 0;
function myLoop() {
setTimeout($.proxy(function () {
text = i + "/12";
updateChart.text(text)
.change()
i = i + 0.2;
var maxValue = $(this).data("value");
if (i <= maxValue) myLoop();
},this), 0.5)
}
myLoop();
});

javascript setInterval on an object's function only calls 2x

var focus = true;
function z() {
this.t = 0;
this.p = function (t) {
if (focus == true) {
this.t = t;
alert(this.t);
}
}
}
var zp = new z();
setTimeout('zp.p(0)', 100);
window.setInterval('zp.p(1)', 2000);
var ftimer = setTimeout('focus=false', 2000);
document.addEventListener('mousemove', function (e) {
clearTimeout(ftimer);
focus = true;
ftimer = setTimeout('focus=false', 2000);
}, false);
I have this code. but for some reason it only alerts twice even with continuous mouse movements. I have been working at this and investigating in firebug and focus is true when I am moving my mouse. I have been trying to figure out what is going on.. even if I do this:
function z() {
this.t = 0;
this.p = function (t) {
if (focus == true) {
this.t = t;
}
alert(this.t);
}
}
It still only alerts twice.
I have tried using a looping setTimeout function too but that doesnt work either. It is driving me crazy.
Good that you found your bug.
I would write your code using a bit more functions-as-first-class-objects and less eval logic:
var focus = true;
function z() {
var that = this; // we won't always have the correct value of "this"
this.focus = true;
this.t = 0;
this.p = function (t) {
if (that.focus == true) {
that.t = t;
alert(t);
}
}
this.fade = ( function(obj){ return function(){ obj.focus = false; } } )(this); // Using self-invokation
}
var zp = new z();
setTimeout(zp.p, 100, 0);
window.setInterval(zp.p, 2000, 1);
var ftimer = setTimeout(zp.fade, 2000);
document.addEventListener('mousemove', function (e) {
clearTimeout(ftimer);
zp.focus = true;
ftimer = setTimeout(zp.fade, 2000);
}, false);
I used a self-invoking function on line 10: ( function(obj){ return function(){...} })(this);, and set this to a different variable.
I found out that it was just related to firefox and that firefox choked on a line of code so that is why it wouldn't run. So I fixed that and now everything is all good.

Categories