I am working on a game using HTML and Javascript. And I need a function to be called every 2 seconds. I tried using setInterval but the function is not being called. And the browser console does not show any errors.
function Apples() {
this.Apples = [];
this.indexCount = 0;
this.CurrentTime;
this.createApples = function() {
this.Apples[this.indexCount] = new Apple();
this.indexCount++;
}
this.updateApples = function() {
for (var i = 0; i < this.indexCount; i++) {
this.Apples[i].positionY += 1;
this.Apples[i].drawApple();
}
}
}
var apples = new Apples;
setInterval(apples.createApples, 200);
It does not work because of the context of this and your code is 200 milliseconds, not 2 seconds.
function Apple () {
return {
positionY: 0,
drawApple: function () {}
}
}
function Apples() {
this.Apples = [];
this.indexCount = 0;
this.CurrentTime;
this.createApples = function() {
this.Apples[this.indexCount] = new Apple();
this.indexCount++;
console.log("new index", this.indexCount);
}
this.updateApples = function() {
for (var i = 0; i < this.indexCount; i++) {
this.Apples[i].positionY += 1;
this.Apples[i].drawApple();
}
}
}
var apples = new Apples;
// use bind
// setInterval(apples.createApples.bind(apples), 2000);
// or use function
setInterval(() => apples.createApples(), 2000);
//setInterval(function () { apples.createApples(); }, 2000);
Related
I am making a screensaver that displays the elements inside an array with a delay between each iteration, to display the elements slowly one by one. My "onmousemove" event successfully removes the screensaver from the page, but the for loop in my startScreensaver() function keeps running when it should in fact break. What am I doing wrong?
see JSfiddle https://jsfiddle.net/m60k75jf/
const idletime = 2;
const screenSaver = document.getElementById("screensaver");
const stars = Array.from(document.querySelectorAll(".star"));
let mousetimeout;
let screensaverActive = false;
window.addEventListener("mousemove", (e) => {
clearTimeout(mousetimeout);
if (screensaverActive) {
stopScreensaver();
} else {
mousetimeout = setTimeout(function () {
startScreensaver();
}, 1000 * idletime);
}
});
function stopScreensaver() {
screensaverActive = false;
stars.forEach((star) => {
star.classList.remove("is--visible");
});
screenSaver.classList.remove("is--active");
}
function startScreensaver() {
screensaverActive = true;
screenSaver.classList.add("is--active");
for (let index = 0; index < stars.length; index++) {
if (screensaverActive) {
setTimeout(function () {
stars[index].classList.add("is--visible");
}, 2000 * index);
} else {
break;
}
}
}
You can't break the loop like that. Your loop creates all these setTimeout immediately. Your condition will just never trigger. What you'll need to do is clear out all those setTimeout. You can push them into an array.
let animationTimeouts;
function startScreensaver() {
animationTimeouts = [];
screensaverActive = true;
screenSaver.classList.add("is--active");
for (let index = 0; index < stars.length; index++) {
if (screensaverActive) {
animationTimeouts.push(setTimeout(function () {
stars[index].classList.add("is--visible");
}, 2000 * index));
} else {
break;
}
}
}
Then clear them out on stopScreensaver
function stopScreensaver() {
screensaverActive = false;
if(animationTimeouts) animationTimeouts.forEach(clearTimeout);
stars.forEach((star) => {
star.classList.remove("is--visible");
});
screenSaver.classList.remove("is--active");
}
You might also want to reconsider moving your CSS transition to .star.is--visible
I'm learning Javascript and testing using Jasmine. I have 2 files. There are more details to the files. This particular section is not working. When I run the test, it fails saying "Expected NAN to equal 10". I wanted to get the totalCount from Counter.js to calculate of MyProject.js, then it is divided by iteration. Can anyone help me with this?
Counter.js
function Counter(){
this.count = 0;
}
Counter.prototype.startCount = function(count){
this.count += count;
};
Counter.prototype.totalCount = function(){
return this.count;
}
MyProject.js
function MyProject() {
this.iteration = 0;
}
MyProject.prototype.addIteration = function(iteration) {
this.iteration += iteration;
}
MyProject.prototype.calculate = function() {
var averageCount = Counter.prototype.totalCount();
return averageCount/this.iteration;
}
MyProject_spec.js
describe("MyProject", function() {
var project, iteration1, iteration2, iteration3;
beforeEach(function(){
project = new MyProject();
iteration1 = new Counter();
iteration1.startCount(10);
iteration2 = new Counter();
iteration2.startCount(20);
iteration3 = new Counter();
iteration3.startCount(10);
});
it("can calculate(the average number of counting completed) for a set of iterations", function(){
project.addIteration(iteration1);
expect(project.calculate()).toEqual(10);
});
When I run the test, it fails saying "Expected NAN to equal 10"
Your problem is addIteration is expecting a number as an argument and you are passing the Counter to the same.
project.addIteration(iteration1);
You need to modify your MyProject in such a way that
function MyProject()
{
this.allIteration = [];
this.totalIterationCount = 0;
}
MyProject.prototype.addIteration = function(iteration)
{
this.allIteration.push( iteration );
this.totalIterationCount += iteration.totalCount();
}
MyProject.prototype.calculate = function()
{
return this.totalIterationCount /this.allIteration.length;
}
Demo
function Counter() {
this.count = 0;
}
Counter.prototype.startCount = function(count) {
this.count += count;
};
Counter.prototype.totalCount = function() {
return this.count;
}
function MyProject() {
this.allIteration = [];
this.totalIterationCount = 0;
}
MyProject.prototype.addIteration = function(iteration) {
this.allIteration.push(iteration);
this.totalIterationCount += iteration.totalCount();
}
MyProject.prototype.calculate = function() {
return this.totalIterationCount / this.allIteration.length;
}
project = new MyProject();
iteration1 = new Counter();
iteration1.startCount(10);
iteration2 = new Counter();
iteration2.startCount(20);
iteration3 = new Counter();
iteration3.startCount(10);
project.addIteration(iteration1);
console.log(project.calculate());
project.addIteration(iteration2);
console.log(project.calculate());
project.addIteration(iteration3);
console.log(project.calculate());
i'm try to make something and i made this piece of code,but when i press the botton it's happend for a sec and then disappear,am i donig passing the arguments wrong or something?
here the code is:
{
var fil1;
var rtextDiv;
for (var i = 0; i < dmsg.getElementsByClassName('refilter').length; i++) {
var refilterInput = dmsg.getElementsByClassName('refilter')[i];
refilterInput.addEventListener('keyup', firstfilter(rtextDiv, fil1,refilterInput));
}
};
function firstfilter(e, rtextDiv, fil1, refilterInput) {
rtextDiv = refilterInput.parentNode.parentNode.getElementsByClassName('rtext')[0];
while (rtextDiv.firstChild) {
rtextDiv.removeChild(rtextDiv.firstChild);
}
fil1 = filteredPropertiesTable(res, refilterInput.value);
rtextDiv.appendChild(fil1);
};
edited as the comment said:
{
var fil1;
var rtextDiv;
for (var i = 0; i < dmsg.getElementsByClassName('refilter').length; i++) {
var refilterInput = dmsg.getElementsByClassName('refilter')[i];
refilterInput.addEventListener('keyup', function()
{firstfilter(rtextDiv,fil1,refilterInput)(rtextDiv, fil1,refilterInput)});
);
}
};
function firstfilter(e, rtextDiv, fil1, refilterInput) {
rtextDiv = refilterInput.parentNode.parentNode.getElementsByClassName('rtext')[0];
while (rtextDiv.firstChild) {
rtextDiv.removeChild(rtextDiv.firstChild);
}
fil1 = filteredPropertiesTable(res, refilterInput.value);
rtextDiv.appendChild(fil1);
};
is it true know?can i pass argument that way?
Here you are actually executing the handler:
refilterInput.addEventListener('keyup', firstfilter(rtextDiv, fil1,refilterInput));
You should just present the handler name:
refilterInput.addEventListener('keyup', firstfilter);
And the handler can be improved:
function firstfilter(e) {
var rtextDiv = this.parentNode.parentNode.getElementsByClassName('rtext')[0];
while (rtextDiv.firstChild) {
rtextDiv.removeChild(rtextDiv.firstChild);
}
var fil1 = filteredPropertiesTable(res, this.value); // you didn't say what is res
rtextDiv.appendChild(fil1);
};
I have a simple class below that starts and then updates a count every second. How would I go about adding functionality for it to listen for a specific value and then fire a callback?
function Counter() {
this.currentCount = 0;
}
Counter.prototype.start = function() {
setInterval(this.update, 1000);
};
Counter.prototype.when = function(value, callback) {
callback(value);
};
Counter.prototype.update = function() {
this.currentCount++;
};
In my mind it would work something like this.
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.start();
This is just a nice homework, I'll do that for you ;) Please have a look on my solution:
function Counter() {
this.currentCount = 0;
this.conditions = [];
this.interval = undefined;
}
Counter.prototype.start = function() {
if (!this.interval) {
var that = this;
this.interval = setInterval(function () {
that.update();
}, 1000);
}
};
Counter.prototype.stop = function () {
if (this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
this.currentCount = 0;
};
Counter.prototype.when = function(value, callback) {
var that = this;
this.conditions.push(function () {
if (that.currentCount === value) {
callback.call(that, value);
}
});
};
Counter.prototype.update = function() {
this.currentCount++;
for (var i = 0, l = this.conditions.length; i < l; i++) {
var condition = this.conditions[i];
condition();
}
};
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.when(60, function (value) {
console.log('Stop at ' + value + '!');
this.stop();
});
counter.start();
and it's fiddled!
Another answer here made a good argument in hiding private variables, but implemented it a bit too confused, so this is another way of doing it similar. Instead of shared prototype functions this is using instance functions. Some may say this needs more memory, but I don't believe it's significant, and allows to easily have privates in a real constructor function.
var Counter = function () {
var that = this, currentCount = 0,
conditions = [], interval;
var update = function () {
currentCount++;
for (var i = 0, l = conditions.length; i < l; i++) {
var condition = conditions[i];
condition();
}
};
this.start = function () {
if (!interval) {
interval = setInterval(function() {
update.call(that);
}, 1000);
}
};
this.when = function (value, callback) {
conditions.push(function () {
if (currentCount === value) {
callback.call(that, value);
}
});
};
this.stop = function () {
if (interval) {
clearInterval(interval);
interval = undefined;
}
currentCount = 0;
};
};
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.when(60, function (value) {
console.log('Stop at ' + value + '!');
this.stop();
});
counter.start();
see it fiddled!
Notice also that in both examples, counter is instanceof Counter and Object,
whereas Counter is instanceof Function and Object (why I like to write so much code ;))
To support multiple whens:
Add an array of whens in your class:
function Counter() {
this.currentCount = 0;
this.whens = [];
}
Then let the when function push to that:
Counter.prototype.when = function(value, callback) {
this.whens.push({'time' : value, 'callback' : callback});
}
And check for these whens at update:
Counter.prototype.update = function() {
this.currentCount++;
for(var w in this.whens) {
if(this.currentCount == this.whens[w].time) {
this.whens[w].callback();
}
}
}
Try something more like:
function Counter(interval, val, func){
this.currentCount = 0;
setInterval(function(){
this.currentCount++;
if(this.currentCount === val)func();
}, interval);
}
var nc = new Counter(1000, 50, function(){
console.log('We have arrived at '+nc.currrentCount);
});
There is an argument to be made for something like this instead:
var Counter = (function() {
var update = function() {
var idx, callbacks;
this.currentCount++;
callbacks = this.callbacks[this.currentCount];
if (callbacks) {
for (idx = 0; idx < callbacks.length; idx++) {
callbacks[idx](this.currentCount);
}
}
};
var start = function() {
var counter = this;
setInterval(function() {update.call(counter)}, 1000);
};
var when = function(count, callback) {
(this.callbacks[count] || (this.callbacks[count] = [])).push(callback);
};
return function() {
var config = {currentCount: 0, callbacks: {}};
this.start = function() {return start.call(config);};
this.when = function(count, callback) {
return when.call(config, count, callback);
};
// this.stop = ... // if desired
};
}());
This is somewhat more memory intensive than the prototype-based version of the code. I wouldn't use it for something where you were expecting hundreds of thousands or millions of objects. But it has the advantage that it truly encapsulates the data you might like to keep hidden, such as currentCount and the list of callbacks. It doesn't expose an unnecessary update function. And it's not terribly more heavy than the prototype version. Each instance has its own start and when functions, but those are just thin wrappers around common functions.
It is a bit more difficult to add a stop function to this in the same manner, unless you don't mind exposing the the result of setInterval. But it is doable.
This is my function, I wrote this for moving my div from left to right. After iter reach 5, the interval has to stop. But now it is keep on running. My interval is not clearing, what is wrong with my function?
var mydiv = document.getElementById('news-variety');
var iter = 0;
if (mydiv) {
var columns = mydiv.getElementsByTagName('DIV');
function animeColumn(index, col) {
var timerID = window.setInterval(function () {
var current = window.getComputedStyle(col);
var matrix = new WebKitCSSMatrix(current.webkitTransform);
col.style.webkitTransform = matrix.translate(10, 0);
if (iter++ == 5) {
window.clearInterval(timerID);
}
}, 50);
}
for (var i = 0; i < columns.length; i++) {
if (columns[i].className.toLowerCase() == "column") {
columns[i];
animeColumn(i, columns[i]);
}
}
}
There is a weird behavior (bug?) in firefox that requires you to specify the second parameter of getComputedStyle to 'null'. Try:
var current = window.getComputedStyle(col,null);
It is likely that in your current case, the code is throwing an 'argument count' error in the timeout handler, which prevents further execution and thus, never clears the timeout.
Alternatively, you may want to do your loop counting at the start of your loop, as such:
function animeColumn(index, col) {
var timerID = window.setInterval(function () {
if (iter++ == 5) {
window.clearInterval(timerID);
}
var current = window.getComputedStyle(col);
var matrix = new WebKitCSSMatrix(current.webkitTransform);
col.style.webkitTransform = matrix.translate(10, 0);
}, 50);
}
I think it is because your var timerID is not defined at window's level
var mydiv = document.getElementById('news-variety');
var iter = 0;
var timerID;
if (mydiv) {
var columns = mydiv.getElementsByTagName('DIV');
function animeColumn(index, col) {
timerID = window.setInterval(function () {
var current = window.getComputedStyle(col);
var matrix = new WebKitCSSMatrix(current.webkitTransform);
col.style.webkitTransform = matrix.translate(10, 0);
if (iter++ == 5) {
window.clearInterval(timerID);
}
}, 50);
}
for (var i = 0; i < columns.length; i++) {
if (columns[i].className.toLowerCase() == "column") {
columns[i];
animeColumn(i, columns[i]);
}
}
}
iter is global, so it will reach 5 for only one of multiple columns.
Try moving it into the function like this:
function animeColumn(index, col) {
var iter = 0;
var timerID = window.setInterval(function () {
// ...
if (iter++ == 5) {
window.clearInterval(timerID);
}
}, 50);
}
i have created a fiddle for this http://jsfiddle.net/DXSNp/