Why does this JavaScript function only work once? - javascript

I have tried hard to find out why this function only runs once, but none of the solutions seem to apply (e.g. declaring a variable the same name as a function). The following function "scroll()" scrolls my page at the required speed to coordinate with the audio reading being played. It works great, but only works once without doing a page refresh.
Where am I going wrong?
function scroll() {
var element = document.getElementById("scrolldiv");
var scrollingheight = element.scrollHeight - window.innerHeight;
var thirdscreen = (window.innerHeight * .31);
var halfscreen = (window.innerHeight * .69);
var delaypercent = (thirdscreen / element.scrollHeight) * 100;
var speedpercent = (halfscreen / element.scrollHeight) * 100;
var dur = $("#jquery_jplayer_audio_1").data("jPlayer").status.duration;
var speedcalc1 = (dur * speedpercent) / 100;
var speedcalc2 = dur - speedcalc1;
var speed = (speedcalc2 * 1000) / scrollingheight;
speed2 = (Math.round(speed * 4) / 4).toFixed(2);
var delay = (delaypercent * dur) * 10;
setTimeout(function(){
scrollinterval = setInterval("scrollmove()", speed2);
}, delay);
}
function scrollmove() {
if (localStorage.getItem("scrolling") == 'on') {
var position = $('#scrolldiv').scrollTop();
$('#scrolldiv').scrollTop(position + 1);
}
}
I realize my code could be written more elegantly, but for now I am wrestling with getting it working. Thanks for any input

As we know setTimeout() will only execute one time, and setInterval() repeats. When you invoke either of these methods, you're creating a variable object that contains the associated callback. When that object is dropped -- setTimeout() exectutes or setInterval() is canceled using clearInterval() -- the object gets nullified and garbage collected. When that happens, the callback it contained and all its variables are zapped too.
Because you're nesting the setInterval() as part of a callback in a setTimeout(), your setInterval() is dropped once the setTimeout() executes. You need to find another way to invoke setInterval();

Related

Simple pure javascript image slider

I'm trying to make the most simple image slider possibly in pure js, basically by changing the background position, but loop doesn't work at all. I need to repeat the loop continuously.
function slider1(){
var slide = document.getElementById("slider-1");
var slideWidth = 320;
var backPosY = 0;
var backPosX = 0;
var sliderEnd = 960;
var f;
for(f=backPosX;f < sliderEnd; f+=slideWidth){
slide.style.backgroundPosition = "-"+f+"px "+backPosY+"px";
}
setInterval(slider1(),1000);}
setInterval takes function as first parameter, but you passed slider1().
Parentheses means calling function so you are calling slider1 and passing to setInterval only returned value of function call.
In your case slider1 does not return anything, that means that setInterval receives undefined instead of your function.
You need to pass the function itself, not results of its calling.
setInterval(slider1, 1000);
But be aware that every call will start a new interval and you don't want to have multiple intervals running calling the same function.
So call setInterval outside of your function or replace setInterval with setTimeout. Timeout will call your function once and your function will create new timeout and so on.
Edit:
Also your for loop loops through all positions in one moment. But you want to move it by one position.
I would do it like this:
var slide = document.getElementById("slider-1");
var slideWidth = 320;
var backPosY = 0;
var backPosX = 0;
var sliderEnd = 960;
var f = backPosX;
function slider1(){
slide.style.backgroundPosition = "-"+f+"px "+backPosY+"px";
f+=slideWidth;
if(f >= sliderEnd)
f = backPosX;
}
setInterval(slider1, 1000);
Edit 2: This code can be used on multiple sliders:
function moveSlider(slide){ // Slide is now a function parameter
var slideWidth = 320;
var backPosY = 0;
var backPosX = 0;
var sliderEnd = 960;
if(!slide.slidePosition) // If there isn't slidePosition in the element
slide.slidePosition = backPosX; // Initialize it
slide.slidePosition+=slideWidth;
if(slide.slidePosition >= sliderEnd) //
slide.slidePosition = backPosX;
slide.style.backgroundPosition = "-"+slide.slidePosition+"px "+backPosY+"px";
}
function startSlider(slide){ // Start slider's interval
return setInterval(function(){
moveSlider(slide);
}, 1000);
}
function stopSlider(intervalId){ // setInterval return ID which you can use to stop the interval
clearInterval(intervalId);
}
You can then start slider with:
startSlider(document.getElementById("slider-1"));
Notice that I need to pass parameter to moveSlider. So I wrapped in another function doesn't take any parameters and calls moveSlider with slider as parameter. This code example can explain it a bit:
function slide1(){
...
}
// Can be also written as:
slide1 = function(){
...
}
I just removed the middle man slide1 and passed it directly.

change the content of a div from inside a javascript class

Hello dear coding buddies,
I have a problem:
Im trying to change the content of a div with the results of a method in a class i made. Somehow i keep ending up with null. I am probably doing something conceptually wrong but i have no idea what since i am pretty new to javascript.
This is my code so far:
class Virus {
constructor(population) {
this.mortalityPercentage = 0.023;
this.period = 1;
this.infectionRate = 2;
this.infected = 1;
this.population = population;
}
calculate() {
while (this.infected < this.population) {
this.period += 6;
console.log(this.period);
this.infected *= this.infectionRate;
console.log(this.infected);
this.dead = this.infected * this.mortalityPercentage;
console.log(this.dead);
}
}
render() {
document.getElementById('days').innerHTML = this.period;
document.getElementById('infected').innerHTML = this.infected;
document.getElementById('dead').innerHTML = this.dead;
}
};
let virus = new Virus(7760000000);
virus.calculate();
virus.render();
How many days does it take to infect the world?
<div id="days">
</div>
Number of infected people:
<div id="infected">
</div>
I see dead people:
<div id="dead">
</div>
This is the error message i get on jsFiddle:
Uncaught TypeError: Cannot set property 'innerHTML' of null
at Virus.render ((index):55)
at (index):63
Ideally i would like a counter making use of setInterval or requestAnimationFrame but i have no idea how. Can you please help?
Your setInterval could go in a bunch of places, but it's probably most clear to put it in around your function calls:
setInterval( () => {
virus.calculate();
virus.render();
}, 250)
Now the calculate and render methods will be executed every 250ms
You can replace the while inside your calculate with an if since it will be evaluated every time the function is called
calculate() {
if (this.infected < this.population) {
this.period += 6;
this.infected *= this.infectionRate;
this.dead = this.infected * this.mortalityPercentage;
}
}
Working example: https://jsfiddle.net/yodjf14v/3/
Alternatively, you could put the setInterval inside the calculate() function and have it call render():
calculate() {
setInterval( () => {
if (this.infected < this.population) {
this.period += 6;
this.infected *= this.infectionRate;
this.dead = this.infected * this.mortalityPercentage;
this.render(); // Here's the render call
}
}, 250)
}
For extra goodness, make the 250ms in your setInterval a property of the you class like this.interval = 250;. Having the value in the middle of the code is known as a magic number and is often considered bad practice.
requestAnimationFrame probably wouldn't be a good match for your use case here because you don't have direct control over when it runs (ie: every n milliseconds). It's generally more used for animation when you want to repaint as often as possible.
You could always throttle your function calls within it by storing the last called time and comparing it to the current time, but for this use case it would just be setInterval with more steps

I'm having a lot of trouble with setInterval and class methods

I keep running into bizarre problems. I've been unable to find anything on them after doing some research, so I thought I'd come here to present them. I have a class which is rather long, but I'll include the relevant bits:
class AnimatedSnake {
constructor(canvasId, coordinates) {
this.coordinates = coordinates;
this.direction = 2;
this.ctx = document.getElementById(canvasId).getContext("2d");
// 0 - .99, describes how far along snake is between coordinates
this.progress = 0;
}
erase() {
for (let i = 0; i < this.coordinates.length; i++) {
let c1 = this.coordinates[i][0],
c2 = this.coordinates[i][1];
this.ctx.clearRect(c1 * 31, c2 * 31, 31, 31);
}
}
next() {
this.progress += 0.01;
if (this.progress >= 1) {
this.progress %= 1;
let nextCoord = this.coordinates[4].slice();
nextCoord[0] += ((this.direction % 2) * this.direction);
nextCoord[1] += ((!(this.direction % 2) * (this.direction / 2)));
this.coordinates.push(nextCoord);
this.coordinates.shift();
}
console.log(this.erase);
this.erase();
this.draw();
}
}
So far, I can call AnimatedSnake.next() indefinitely if I'm doing it manually (i.e. from the console). However, when I put the function in an interval or timeout - setInterval(AnimatedSnake.next, 100) - it all of a sudden, on the first run, claims that AnimatedSnake.erase is not a function. I tried putting AnimatedSnake.erase() directly in the interval, and when I do THAT, for some absurd reason it goes and tells me that it cannot take the length property of AnimatedSnake.coordinates, which it claims is undefined. Nowhere in my code to I redefine any of these things. coordinates is altered, but it should not be undefined at any point. And erase is of course a method that I never change. Does anyone have any insight into why, when these are called with setInterval or setTimeout weird things happen, but if I call the functions repeatedly (even in a for loop) without the JavaScript timing functions everything works out fine? I'm genuinely stumped.
Consider these two snippets:
animatedSnake.next()
And:
let method = animatedSnake.next;
method();
In the first snippet next is called as a member of animatedSnake object, so this within the context of next method refers to the animatedSnake object.
In the second snippet the next method is detached from the object, so this no longer refers to the animatedSnake instance when the method function is invoked. This is how passing a method to another function, like setInterval works. You can either use Function.prototype.bind method for setting the context manually:
setInterval(animatedSnake.next.bind(animatedSnake), 100)
or wrap the statement with another function:
setInterval(() => animatedSnake.next(), 100)

Unity javascript infinite spawning

So I'm trying to set up a system in a project where these spawn points will spawn targets that move towards the player and have to be destroyed before reaching a certain point, or its game over. Everything seems to be working fine except for one issue. The spawners don't stop spawning. They're supposed to do waves, spawning more enemies after each wave has been finished. I'm totally lost as to where the error might be.
Small note, originally I had the spawn count be 3 times the enemyspawncount, and spawnCount would count down to 0, then jump to 2 and remain there.
Spawner script:
var targetPrefab:Transform;
var spawnCount = deathcounter.enemySpawnCount;
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while (true)
yield CoUpdate();
}
function CoUpdate(){
spawnCount = deathcounter.enemySpawnCount;
while(spawnCount > 0)
{
var target= Instantiate(targetPrefab, transform.position, transform.rotation);
target.rigidbody.AddForce(Vector3.right * (deathcounter.enemySpawnCount *0.5 * 100));
spawnCount = spawnCount - 1;
Debug.Log("Spawn" + spawnCount);
yield WaitForSeconds (5);
}
deathcounter.timeToSpawn = false;
}
Target script:
var spawnCount = deathcounter.enemyDeathCount;
function OnTriggerEnter() {
Destroy (gameObject);
deathcounter.enemyDeathCount = deathcounter.enemyDeathCount + 1;
}
Death Counter script:
static var enemyDeathCount = 0;
static var enemySpawnCount = 1;
static var timeToSpawn : boolean = true;
function Update () {
if(enemyDeathCount % 3 == 0 && enemyDeathCount != 0){
timeToSpawn = true;
enemySpawnCount = enemySpawnCount + 1;
}
}
The issue could be in function CoUpdate(). The value of deathcounter.enemySpawnCount never gets reduced in that function. So if CoUpdate() gets called again, deathcounter.enemySpawnCount will still have the same value, and more enemy prefabs will get instantiated.
If that is the issue, and I'm not just misreading your code, you can solve that easily by setting deathcounter.enemySpawnCount after you set spawnCount:
spawnCount = spawnCount - 1;
deathcounter.enemySpawnCount = spawnCount;
Debug.Log("Spawn" + spawnCount);
Debug.Log("Spawn (double-check) " + deathcounter.enemySpawnCount);
With much more mature eyes, I can look back and correct myself.
First I have to flip the order of these commands so they both trigger.
function OnTriggerEnter() {
deathcounter.enemyDeathCount = deathcounter.enemyDeathCount + 1;
Destroy (gameObject);
}
Second I have to redo how spawnCount is handled. It should be removed from the 'target' script, and given a set initial value, not set to another variable value. It should only be changed in the while loop with each iteration, and in the death counter script, inside the if statement, so it is set to be equal to the new enemySpawnCount value only when that if statement is true.

Generate random number every x seconds and display it

Inside my App I have a sort of bottom bar Always shown, where I have a div with an h1, and a button. Everytime I click the button I coded and animation that do change the text of the h1 with a random number. Till here everything works fine.
My goal is to make this thing WITHOUT pressing a button, but just every x seconds during all the App execution. I tried to use "setInterval(func, ms);" I didn't get any error, but it just did it one time.
I think that my error is "where to put" the code. I don't understand this. So, I tried to put the code on the event handler of my button just to see if it works, but it did it one time anyway. I need that this function continues to be executed while the users do something else... It's like a clock inside an app: it should work continuosly while you do something else.
My function code:
function fraseCasuale(sel) {
var i = 1 + Math.floor(Math.random() * (groupedItems.length-1));
var oggetto = groupedItems.getAt(i);
if (sel === 1) {
document.getElementById("frasecasuale").textContent = oggetto.frase;
document.getElementById("autore").textContent = oggetto.nome;
document.getElementById("genere").textContent = oggetto.genere;
} else {
document.getElementById("frasecasuale2").textContent = oggetto.frase;
document.getElementById("autore2").textContent = oggetto.nome;
document.getElementById("genere2").textContent = oggetto.genere;
}
}
And then how I call it, for example:
setInterval(fraseCasuale(1), 5000);
My application is based on the grid template of Visual Studio 2012 for Windows Store aps. I added the bar I was talking above into "default.html", which is the container of the other pages.
Are you sure something like this doesn't work?
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var ms = 2000;
var func = function () {
var randNum = getRandomInt(1, 253); // Gets random number between 1 and 253
document.getElementById("element_id").innerHTML = randNum;
};
setInterval(func, ms);
http://jsfiddle.net/FQSAH/1/
The problem in your code is that you aren't passing a function to setInterval, you are passing the ONE TIME result of the function call fraseCasuale(1), so the result of that function is what is being passed to setInterval.
Since you want to pass a parameter to it, setTimeout lets you specify arguments after the function pointer and timeout interval, such as:
setInterval(fraseCasuale,5000,1);
If you are doing this in a defined 'Page' you'd do something like this in home.js
WinJS.UI.Pages.define("/pages/home/home.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
setInterval(this.fraseCasuale, 1000, 656);
},
fraseCasuale: function (sel) {
console.log(sel);
var i = 1 + Math.floor(Math.random() * (4 - 1));
var element = document.getElementById("updateMe");
element.textContent = i;
}
});
or if just using a default.js then you can throw your code there in app.ready, right above for ex. app.oncheckpoint (order doesn't actually matter above or below any function, just providing a place to show you an example)
So in default.js:
app.onready = function (args) {
//1 is a placeholder for your parameter
setInterval(fraseCasuale,5000,1);
};
This should work...
var id = window.setInterval(function(){randomNumber()},1000);
function randomNumber()
{
var rand = Math.floor(Math.random()*6)
//Do whatever you want with that number
}
Quick JSBin : http://jsbin.com/egajog/1/

Categories

Resources