Understanding loops in Java Script - javascript

This is my code, I'm not sure whether I am using the correct loop but this is basically want to do. I am trying get every src in the array to print out every one second, but the myPix[thisPic] becomes undefined when attempting to print out the second item in the myPix Array. I'd really be happy to get some help. Thank you!
function nextPic() {
count = -1;
thisPic = 0;
if (thisPic == count) {
document.write("This shouldn't ever happen");
return false;
} else {
count++;
var myPix = new Array("images/slideshow/Jellyfish.jpg", "images/slideshow/Penguins.jpg", "images/slideshow/Lighthouse.jpg");
slideTag = document.getElementsByTagName("img")[1];
slideTag.src = myPix[thisPic];
currPic = slideTag.src;
document.write(myPix[thisPic] + "<br />");
document.write(currPic);
}
return false;
}
setInterval(nextPic, 1000);

Code doesn't work because "loop" has no shared variables. Every iteration should work on the same array and the same iterator, in Your code every iteration created new array and new iterator, so every iteration was doing the same thing.
To work correctly every loop iteration must:
Take current image src from array ( one variable declared outside loop )
Set it on img element
Change current variable to current+1 ( current variable delared outside loop )
If our current is more that array size then - end (clearInterval) or set current on 0 - then will start again and again ...
//array declaration should be outside of loop
var myPix = new Array("https://www.gravatar.com/avatar/50fbdd1dcbe4763a33a0e212f6a632c8?s=32&d=identicon&r=PG&f=1", "https://www.gravatar.com/avatar/5be6299fb284071a9fd2cc2cc0b9090a?s=32&d=identicon&r=PG", "https://i.stack.imgur.com/mlB9c.jpg?s=48&g=1");
var currentPic=0; //this is number of current picture
function nextPic(){
if (currentPic>= myPix.length){
// here example how to stop:
//clearInterval(interval);
//return;
//we loop it over again from begging
currentPic=0;
}
//get img in page - image by currentPic
slideTag = document.getElementsByTagName("img")[1];//second
//set src on this slideTag from array
slideTag.src = myPix[currentPic];
currentPic++;
}
var interval=setInterval (nextPic, 1000);//save interval in variable to use it to stop
<h1>images</h1>
<img><br/>
<img><br/>
<img><br/>
I taken example avatars from stackoverflow to show working example :)

As per understanding your code, I have figurout your issue
var myPix = ["images/slideshow/Jellyfish.jpg", "images/slideshow/Penguins.jpg", "images/slideshow/Lighthouse.jpg"];
counter=0;
interval = setInterval (nextPic, 1000);
function nextPic(){
if(counter >= myPix.length){
document.write("This shouldn't ever happen");
clearInterval(interval);
}
else{
slideTag = document.getElementsByTagName("img")[1];
slideTag.src = myPix[counter];
currPic = slideTag.src;
document.write(myPix[thisPic] + "<br />");
document.write(currPic);
counter++;
}
}

Related

How to update a variable that is returned from a function?

I've been trying to write a go back function. What this function will do is it'll store last two ID number that has been generated and append when there is a new one, delete the first one.
I have this function which creates the IDs and plays the video with that ID.
function newVideo(){
let rando = Math.floor((Math.random() * 3970) + 1);
document.getElementById("vsrc").src = "https://raw.githubusercontent.com/ondersumer07/vinematik-videos/master/vid/" + rando + ".mp4";
document.getElementById("videoEl").load();
return rando;
}
I am returning the rando to use it outside this function, and I can access it outside the function, the problem is, the variable outside the function is not updating everytime newVideo() run.
The code for that goes like this:
let rando = newVideo();
let vidids = [];
if (vidids.length < 2) {
vidids.push(rando)
console.log("added")
} else {
vidids.shift()
console.log("deleted")
};
Basically what this does is to get the returned rando value and push it to the vidids array, delete the first one if there is more than two but it won't, it does not update the let vidids = []; array for some reason. I need it to update everytime the newVideo() function ran.
Also, I want the if to add if there is less then two items in that array and delete from the start of the array if there is more than two items in it. Not really sure if that'll work too.
I can't seem to figure out how to do this, am I doing this whole thing wrong or is there still hope for this function? How am I supposed to write that function?
Edit: I've changed the vidids.length part yet the problem still occur because let rando = newVideo(); doesn't update.
In order to keep the last two Items of an array, you can use something like this.
let vidids = [];
function addToArray(item, array){
array.unshift(item);
return array.slice(0,1);
}
//test
vidids = addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
vidids =addToArray(newVideo(), vidids);
console.log('Done');
You've made a small mistake that caused your code to not work properly.
The IF condition is intended to check the length of the 'vidids' array,
What actually happens is that it compares the array referance instead of it's length
To fix the issue, add .length after 'vidids' inside of the IF condition.
....
if (vidids.length < 2) {
.....
I figured it out. Basically what I did is storing every number the algorithm creates and then taking one before the last.
Which goes like this:
The algorithm for creating random numbers:
function randomNum() {
let rando = Math.floor((Math.random() * 3970) + 1);
return rando;
};
Then I put this function in a variable:
let videoid = randomNum()
I had another variable called videoids which is above and outside of the randomNum function and is an array:
let videoids = []
After that I stored every number I created with videoid inside videoids by pushing it(This push needs to be inside your function):
videoids.push(videoid);
Okay so I stored all the numbers this way. Now I should take one before the last so I can go to previous video. So I needed to create a function, I used this function which was created by Tadeck, in this thread.
if (!Array.prototype.last) {
Array.prototype.last = function() {
return this[this.length - 2];
};
};
Now I can put that inside of my prevVideo function which looks like this:
function prevVideo() {
document.getElementById("vsrc").src = srcRaw + videoids.last() + ".mp4";
document.getElementById("videoEl").load();
videoids.push(videoids.last());
}
Note: Don't forget to push videoids.last inside your videoids otherwise you can only go to your previous number for once.

How to grab an objects property that is stored in an array on a click event

I have this Object and I spawn it onto the canvas after one is picked and pushed into my array.
function grabObjProp(){
var randomInt = Math.floor(Math.random() * 10) + 1;
goodProp = {};
goodProp["name"] = "goodguy";
goodProp["MC"] = new lib.goodObj();
badProp = {};
badProp["name"] = "badguy";
badProp["MC"] = new lib.badObj();
if(randomInt < 7)
ObjArray.push(goodProp);
else
ObjArray.push(badProp);
createObj();
}
I then run an if/else to see if I am clicking the good or the bad and based off that I then either run a gameOver function or I continue the game. My current issue is that my if/else checks the last array that was added and if I badguy is added when I click a goodguy the condition is met and the game ends. I wanted to know how to get the name of the object I clicked store it to a variable then compare that variable when I click and object.
function Click(e){
if(ObjArray[ObjArray.length - 1].name != "goodguy"){
addGameOverScreen();
}else{
numClicked +=1;
scoreTxt.text = numClicked;
exportRoot.removeChild(e.currentTarget);
grabObjProp();
//}
}
Edit** added -1 to if(ObjArray[ObjArray.length].name != "goodguy")so that the if was always check the last index of the array.

counter variable is holding 2 values on 2nd pass of the function

I'm making a typing game. When multiple players play the game it runs through the same set of functions again. I'm using the variable j as a counter to advance words when they are typed correctly. For some reason, on the second pass on each upkeystroke, it logs j = 1 & j = whatever the value of the previous players last word + 1 is. When each player plays, I want each set of words they are typing to be the same, so that it is fair. I can't figure out why this is happening or even how the variable has 2 values at the same time?!?!?
What gives?
Here's the code in question, but there's a bunch of callbacks that could be involved, although the only place this variable is called is inside this function.
//advances ship on correct typing
function runRace() {
timer();
var j = 1;
//BUG HERE !! Works fine on first iteration but on second
//iterations value jumps beteween 1 and whatever the next
//one is. It's like on every keystroke it reassigns var j
//back to 1, then back to the element number it was on
//last time
//!!! j has 2 values !!!it's keeping the value from the
//prior running of run race
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
}
If you need to see the rest of the code to figure this out, it's here. Eventhough it's not running right on jsfiddle for reason, it works other then the bug, locally https://jsfiddle.net/ujsr139r/1/
As i mentioned in my comment you're creating multiple listeners everytime runRace() is called.
You could try something like this instead (please note, this isn't the best way to do this, i'm just demoing. Global variables like j in this case aren't a clever idea.:
var j=1; // global because its outside of your function
$(function(){
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
});
//advances ship on correct typing
function runRace() {
j = 1;
timer();
}

setInterval() change image

The goal: When the page is loaded, display the image andy_black.jpg. After two seconds, change the image source, and the thus image in the browser, to a second image called andy_white.jpg. This will change back and forth every 2 seconds.
I checked out this article:
SetInterval function calls
(I searched other as well, with the tags [javascript] [function] and the word "setinterval", but most were using jQuery and my intention here is not to use any jQuery, it's an experiment in JavaScript after all).
which was quite helpful for before I had read it my code was much longer and the function was not called in the setInterval() function.
So here's some code:
Suggestions?
var i = 1;
function change_pic() {
i + 1;
if (i == 5) {
i = 1;
}
//I suspect the computer will read i as 5 for some
//tiny amount of time before reverting back to 1
//which I suspect could cause a further problem, but
//is it the source of the current issue?
if (i == 1 || i == 2) {
document.getElementById('img_to_flip').src = "https://cdns-images.dzcdn.net/images/artist/5d9e44027cc266260d7bd932d98f739d/500x500.jpg";
} else {
document.getElementById('img_to_flip').src = "https://media.s-bol.com/q7R3B8QVrAj2/550x549.jpg";
}
}
var pic_src = setInterval(change_pic, 2000);
<img id="img_to_flip" src="https://media.s-bol.com/q7R3B8QVrAj2/550x549.jpg" height="100" width="100" />
You forget to actually reassign the new value to i.
Either use:
i = i + 1;
or
++i;
Also, why count to five when you only have two states? A common paradigm to have an auto-resetting counter is to use modulo arithmetic:
i = (i + 1) % 2;
which guarantees that i will only ever have values of 0 or 1.
FWIW, here's an alternate way of writing the entire feature that'll work for any number of images - just populate the pics array:
(function() { // function expression closure to contain variables
var i = 0;
var pics = ["https://media.s-bol.com/q7R3B8QVrAj2/550x549.jpg", "https://cdns-images.dzcdn.net/images/artist/5d9e44027cc266260d7bd932d98f739d/500x500.jpg"];
var el = document.getElementById('img_to_flip'); // el doesn't change
function toggle() {
el.src = pics[i]; // set the image
i = (i + 1) % pics.length; // update the counter
}
setInterval(toggle, 2000);
})(); // invoke the function expression
<img id="img_to_flip" src="https://media.s-bol.com/q7R3B8QVrAj2/550x549.jpg" height="100" width="100" />
If you want to avoid the delay in first time setInterval call the function before the setInterval as shown in the top answer:
(function() { // function expression closure to contain variables
var i = 0;
var pics = [ "andy_white.jpg", "andy_black.jpg" ];
var el = document.getElementById('img_to_flip');
function toggle() {
el.src = pics[i]; // set the image
i = (i + 1) % pics.length; // update the counter
}
toggle()
setInterval(toggle, 2000);
})(); // invoke the function expression

Using shift() and push() to loop array values vs. using a counter variable, what is the best approach?

I'm looping through a set of images within an array of animation frames. There are 7 images, and looping from 1-7 completes the animation. I need this animation to loop indefinitely, but I was wondering which of these is the best approach:
Loop by modifying array
/* Pull image from start of array. */
var image = frames.shift();
/* Process image. */
...
/* Add image back to end of array. */
frames.push(image );
Loop using counter variable
/* Pull image by counter offset. */
var image = frames[counter];
/* Process image. */
...
/* Increment or reset counter value. */
counter + 1 === frames.length ? counter = 0 : counter = counter + 1;
Is there a reason I'd chose one over the other? Alternatively, is there a better approach to this?
Modifying the array is going to be more expensive than simply using a variable to keep track of your position in the array. The better way to do this, if you're looping indefinitely, seems to just be to use a while loop (rather than using a for loop where you reset the counter inside):
var i = 0;
while (true) {
doSomething to array[i];
i = (i+1) % array.length;
}
However if your goal really is having an animation proceed indefinitely every time a given interval elapses, a loop isn't ideal at all. Use setInterval instead.
var frames = ...; //your images
var i = 0;
function animate() {
do something to frames[i];
i = (i+1) % array.length;
}
setInterval(animate, time_between_runs);
where time_between_runs is how much time should elapse before the function is called again.
Alternatively a circular linked list also can be used I think. To turn an array of objects into a circular linked list:
frames.forEach(function(elem, index) {
elem.next = frames[index + 1] || frames[0];
});
And now you can do something like this:
setInterval(function() {
frame = frame.next;
....
}, delay);
One possibility is to ditch the array and use a linked list.
Make each frame an object that points to the next object. The last one then points to the first. Then all you need to do is reference the next object.
var curr = first; // reference to first frame object
setInterval(function() {
// process image
curr.image.doSomething();
// proceed to next
curr = curr.next;
}, 1000);
No counters to mess with this way.
Setting up the linked list is usually pretty simple, and can likely be done with just a little modification to the current code that's setting up the Array.
var first = new Frame(); // This is your entry point
var current = first; // This holds the current frame during setup
for (var i = 0; i < totalFrames; i++) {
current.next = new Frame(); // Make the current reference a new frame
current = current.next; // Make the new frame current
}
current.next = first; // circular reference back to the first.
function Frame() {
// set up this frame
}
Then first is your starting point.
Or the linking could be done within the Frame constructor.
var first = new Frame(null);
var current = first;
for (var i = 0; i < totalFrames; i++) {
current = new Frame(current);
}
current.next = first;
function Frame(currFrame) {
// link this frame to the given one
if (currFrame)
currFrame.next = this;
// set up the rest of this frame
}

Categories

Resources