Loop a function while key is pressed - javascript

I'm trying to move a div up and down using two keys in Javascript. The idea is that while a certain key is depressed, a function loops and adds to the div's 'top' style value each time. The basic function works, but I can't get it to loop and I can't get anything to respond to a keypress.
It's hard to find info on keypress handling in Javascript, it seems most people use jQuery to handle that.
Is my use of the do-while loop correct? is there a better way to handle keydown and keyup events?
Here's my code:
var x = 0;
console.log(x);
function player1MoveDown() {
var value = document.getElementById("player1").style.top;
value = value.replace("%", "");
value = parseInt(value);
value = value + 1;
value = value + "%";
document.getElementById("player1").style.top = value;
console.log(value);
} //moves paddle down; adds to paddle's 'top' style value
function player1MoveSetting() {
x = 1;
do {
setInterval(player1MoveDown(), 3000);
}
while (x == 1);
console.log(x);
} //paddle moves while x=1; runs player1MoveDown function every 3 seconds
function player1Stop() {
x = 0;
}
And here's the relevant bit of HTML:
<div class="paddle" id="player1" style="top:1%" onkeydown="player1MoveSetting()" onkeyup="player1Stop()"></div>

You cannot attach a keydown event to a div, unless it has a tabindex:
<div class="paddle" id="player1"
onkeydown="player1MoveSetting()"
onkeyup="player1Stop()"
tabindex="1"
>
</div>
You can replace all this code:
var value = document.getElementById("player1").style.top;
value = value.replace("%", "");
value = parseInt(value);
value = value + 1;
value = value + "%";
document.getElementById("player1").style.top = value;
… with this:
var p1= document.getElementById('player1');
p1.style.top= parseInt(p1.style.top)+1+'%';
This calls the return result of player1MoveDown:
setInterval(player1MoveDown(), 3000);
Since player1MoveDown doesn't return anything, it's the equivalent of
setInterval(null, 3000);
To call the function every 3 seconds, do this instead:
setInterval(player1MoveDown, 3000);
This creates an infinite loop:
x = 1;
do {
setInterval(player1MoveDown, 3000);
}
while (x == 1);
Even though keyup will set the global x to 0, it will never run because the loop never ends.
Instead, create a timer variable, which is set on keydown and cleared on keyup.
Complete JavaScript Code
var timer;
function player1MoveDown() {
var p1= document.getElementById('player1');
p1.style.top= parseInt(p1.style.top)+1+'%';
console.log(p1.style.top);
}
function player1MoveSetting() {
if(timer) return;
timer= setInterval(player1MoveDown, 100);
}
function player1Stop() {
clearInterval(timer);
timer= null;
}
document.getElementById('player1').focus();
Working Fiddle

Related

How can I set timers in slideshow to show as selected?

I have to create a slideshow, using an array of images and have that set on a timer. There is a drop-down menu with slow, medium, and fast options and the pictures need to transition with accordance to the drop down option selected. Whenever I execute this code in a web browser the code repeats itself, while doubling, as I read the value of i in the console.
I have tried using a while and a do-while loop to have the images on a rotation.
I have also tried putting the if-statements outside and below/above the function.
<script>
var i = 0;
function changeImg(){
if (x == 'slow'){
setInterval("changeImg()", 5000);
} else if (x == 'medium'){
setInterval("changeImg()", 3000);
} else if (x == 'fast') {
setInterval("changeImg()", 1000);
} else {}
while (i < 3){
console.log(i);
document.slide.src = sportsArray[i];
i++;
}
console.log(i);
console.log(sportsArray);
}
</sctipt>
First, I would read up on MDN's docs on setInterval() and clearInterval to fill in the knowledge gaps that lead you to approach the problem this way.
You are recursively calling changeImg() in your code which I believe is causing the issue you describe as:
the code repeats itself, while doubling, as I read the value of i in the console
Also, your while loop will run immediately when calling changeImg() which also does not appear to be desired in this situation.
setInterval() mimics a while loop by nature. There is no need for a while loop in this code. Below is a solution that I hope you can use as a reference. I separated the code to determine the interval into a function the getIntervalSpeed.
function changeImg(x) {
var getIntervalSpeed = function(x) {
if (x === 'slow') {
return 5000;
} else if (x === 'medium') {
return 3000;
} else if (x === 'fast') {
return 1000;
} else {
return 3000;
// return a default interval if x is not defined
}
};
var i = 0;
var slideShow = setInterval(function() {
if (i >= sportsArray.length) {
i = 0; // reset index
}
document.slide.src = sportsArray[i]; // set the slide.src to the current index
i++; // increment index
}, getIntervalSpeed(x));
// on click of ANY button on the page, stop the slideshow
document.querySelector('button').addEventListener('click', function() {
clearInterval(slideShow);
});
}

setTimeout in a for loop with array as an argument misbehaving

I have been working on my second game, which works as follows:
There is a button and its position changes randomly every x seconds
The x seconds is controlled by setTimeout
Whenever the user clicks on the button their score increments by 10
When the score reaches 30 startGame calls Initialize, which creates a new button using JavaScript and pushes the button to an array called numOfBox
The control passes back to startGame with the updated numOfBox, which now has two buttons and changes the top/left coordinates every x seconds
The function is being called by setTimeout multiple times every x seconds instead of once every x seconds. How do I call the function only once every x seconds?
var box=document.getElementById('boxId');
var main=document.getElementById('Maincont');
var timeout=[];
var cnt=0;
var scr=0;
var levelCnt=30;
var divcnt=0;
var numOfbox=[];
var fcnt=0;
function createDiv(Name,Width,Height,Background,Margin,Padding) {
var t=t+divcnt;
divcnt+=1;
var Name=Name+divcnt;
var Nameid='boxId'+divcnt;
Name=document.createElement('button');
Name.id=Nameid;
console.log('IN CREATEDIV :-'+ Nameid+':::'+Name+' cnt '+ cnt);
Name.style.width=Width;
Name.style.height=Height;
Name.style.background=Background;
Name.style.margin=Margin;
Name.style.padding=Padding;
Name.style.boxSizing='border-box';
Name.style.position='absolute';
Name.style.top='10px';
Name.style.left='10px';
Name.style.color='white';
Name.style.textAlign='center';
Name.style.fontSize='15px';
Name.style.textDecoration='none';
Name.style.cursor='pointer';
Name.disabled=true;
t=document.createTextNode('HIT ME');
Name.appendChild(t);
Name.addEventListener('click', function() {
this.style.background='black';
scr+=10;
cnt+=10;
this.innerHTML= 'SCORE = ' +String(scr);
});
var b=document.getElementById('Maincont');
// FIRST HIT ME BOX IS READY NOW....
b.appendChild(Name);
// Array numOfbox loaded with all the Box id's.
numOfbox.push(Name.id);
console.log('PUSHING DIV:-'+ Name.id + " IN ARRAY :=" +numOfbox);
// creatediv is called when score is scr%30==0.
if (numOfbox.length > 1) {
return Name.id;
}
}
function Initialize() {
if (main.childNodes.length < 5) {
if ((cnt > 0) && (cnt %30 ==0)) {
var id='targetCont'+divcnt;
divcnt+=1;
boxId=createDiv(id,'130px','50px','black','0px','0px');
console.log('Inside Initialize:-'+id+' cnt '+ cnt);
cnt=0;
}
startGame();
}
}
function startGame() {
var d=new Date();
var t=d.getMinutes()+':'+d.getSeconds();
fcnt+=1;
console.log('TIME IS:-'+ t + " cnt:-" +fcnt+' INSIDE STARTGAME:-'+numOfbox+':'+numOfbox.length+ ' CNT'+cnt);
for (var i=0 ; i < numOfbox.length ; i++) {
if ((cnt > 0) && (cnt %30 ==0)) {
Initialize();
} else {
console.log('STARTING GAME FOR DIV:='+numOfbox[i]);
var box=document.getElementById(numOfbox[i]);
console.log(box);
box.disabled=false;
var max=500;
var min=10;
var topRand=(Math.floor(Math.random() * (max-min+1) + min));
var max=1200;
var min=10;
var leftRand=(Math.floor(Math.random() * (max-min+1) + min));
box.style.background='black';
box.style.top=topRand+'px';
box.style.left=leftRand+'px';
console.log('CNT:='+cnt);
timeout=setTimeout(startGame,10000,numOfbox);
}
}
}
function stopGame() {
clearTimeout(timeout);
console.log('IN STOPGAME:-'+timeout+' length '+timeout.length);
for (var i=0 ; i < numOfbox.length ; i++) {
console.log(timeout[i]);
clearTimeout(timeout[i]);
var box=document.getElementById(numOfbox[i]);
box.style.background='red';
box.style.top='10px';
box.style.left='10px';
}
timeout=[];
}
I would prefer to do this in JavaScript and not in ECMA5/6 or jQuery, or using arrow functions. I can provide the rest of the code if needed.
I noticed a couple of issues with your code.
You are calling setTimeout in a loop. So if you have, say, numOfbox.length === 15, setTimeout will be called 15 times around 10,000 ms from when you set it. Could this be the reason you're seeing the more calls to startGame than you thought?
I see the variable numOfbox, but since it's not declared in the startGame function I'll have to assume that it's in the parent scope. So, in the line where you do
timeout[i] = setTimeout(startGame, 10000, numOfbox);
Realize that since numOfbox is in a higher level scope and startGame does not take any parameters, the numOfbox parameter (the 3rd argument in setTimeout) is really not going anywhere: the numOfbox variable actually uses is coming from the parent scope. This may be ok, but you should consider what is happening here.

eventListener is not triggering

I have the following code:
function expandTextarea () {
var textareas = document.getElementsByTagName('textarea');
for (var i = 0; i < textareas.length; i++) {
if (textareas[i].scrollHeight > 100) {
textareas[i].style.height = textareas[i].scrollHeight + 2 + 'px';
} else {
textareas[i].style.height = '100px !important';
}
textareas[i].addEventListener('onchange', adjustTextareaHeight(this));
// textareas[i].addEventListener('keyup', adjustTextareaHeight(textareas[i]));
// textareas[i].addEventListener('paste', adjustTextareaHeight(textareas[i]));
// textareas[i].addEventListener('cut', adjustTextareaHeight(textareas[i]));
}
}
function adjustTextareaHeight (textarea) {
console.log(textarea.length);
if (textarea.length > 0) {
textarea.style.height = textarea.scrollHeight + 2 + 'px';
}
}
For some reason the event listener is never fired. Note that expandTextarea is called from my window.onload = function () {} function.
When I call expandTextarea directly it runs without any issues but when I make a change, pressing enter a bunch, it never calls the onchange event.
You have to pass a function to addEventListener. Currently you are passing undefined, the return value of adjustTextareaHeight. So the browser doesn't know what to execute when the event occurs.
What you want is:
textareas[i].addEventListener('change', function() {
adjustTextareaHeight(this);
});
In your code you are calling adjustTextareaHeight immediately (not on change). this likely refers to the global object (window) and window.length is undefined, which is not > 0.

I keep receiving Uncaught TypeError: Object [object global] has no method error during function call

I created a function that would do something unique on it's 3rd call. In this case, alert the incremented number. However, when trying to call this function from another function:
"MyFunction();", I get Uncaught TypeError: Object [object global] has no method 'MyFunction'. Can you please tell me what I'm doing wrong?
var counter = 0;
var num = 0;
function = MyFunction() {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}
I've also tried removing the = sign, as you can see here http://jsfiddle.net/DN3yC/6/ it doesn't work.
LIVE DEMO
Just remove the = sign function MyFunction() { and make sure in your fiddle that JS <script> is in the right placeSee additional explanation below.
Example:
var element = document.getElementById('button');
var counter = 0;
var num = 0;
function MyFunction() {
counter = ++counter % 3; // loop count
if ( !counter ) {
num++;
alert('the new number is: ' + num);
}
}
//On click:
element.addEventListener('click', MyFunction, false);
your new fiddle: http://jsfiddle.net/DN3yC/7
The old one of yours was not working cause you did not used No Wrap - in body for the JS in your fiddle. (See options panel at the left up corner)
So basically you need to put your <script> tags before your </body> closing tag in order to make sure the DOM is ready and parsed by JavaScript before elements manipulation.
Syntacs looks wrong to me, try
var foo = Myfuntion();
or
var foo = new MyFuntion();
depending on what MyFuntion() is.
It should be:
var MyFunction=function () {
// increment outside counter
counter++;
if (counter === 3) {
// do something every third time
num++;
alert('the new number is: ' + num);
// reset counter
counter = 0;
}
}

jQuery each function parseInt loop

My code is supposed to make a set of divs with classes that start with status- fade out then change the classes from ie status-1 to status-11 by adding 10 the number. Everything is working except that the parseint is looping and the classes are becoming status-179831, status-179832...
$(function disappear(){
$('[class^="status-"]').delay(5000).fadeOut(400)
$('[class^="status-"]').each(function(){
num = $(this).attr('class').split('status-')[1];
num = parseInt(num, 10) + 10;
$(this).attr("class", "status-"+num+"");
})
$('[class^="status-"]').delay(1000).fadeIn(400)
disappear();
})
To get your operations to work in sequential order, you need to use the completion function of your animations like this:
$(function (){
function runOne(item) {
item.delay(5000).fadeOut(400, function() {
var num = item.attr('class').split('status-')[1];
num = parseInt(num, 10) + 10;
item.attr("class", "status-"+num+"")
.delay(1000).fadeIn(400, function() {runOne(item)});
});
}
// start all the animations
$('[class^="status-"]').each(function() {
runOne($(this));
});
})
Working demo: http://jsfiddle.net/jfriend00/k7aS7/
As your code was written, the two animations are asynchronous and your .each() loop or the next call to disappear() do not wait for the animations to finish. Using the completion functions like this triggers the next part of your sequence when the animation is done. You also want to always make sure you're using var in front of local variables to avoid accidentally making them global variables.
You can also synchronize a promise object which will guarantee that all the animations always start at the same time on each iteration:
$(function disappear() {
var all = $('[class^="status-"]');
all.delay(5000).fadeOut(400, function() {
var item = $(this);
var num = item.attr('class').split('status-')[1];
num = parseInt(num, 10) + 10;
item.attr("class", "status-"+num+"")
item.delay(1000).fadeIn(400);
})
// when all animations are done, start the whole process over again
all.promise().done(disappear);
})
Working demo of this option: http://jsfiddle.net/jfriend00/SY5wr/
To restore the class names to the original class name after each iteration, you could do this:
$(function () {
// save original class names
var all = $('[class^="status-"]').each(function() {
$(this).data("origClassName", this.className);
});
function disappear() {
all.delay(5000).fadeOut(400, function() {
var item = $(this);
var num = item.attr('class').split('status-')[1];
num = parseInt(num, 10) + 10;
item.attr("class", "status-"+num+"")
item.delay(1000).fadeIn(400);
})
// when all animations are done, start the whole process over again
all.promise().done(function() {
// restore class names
all.each(function() {
this.className = $(this).data("origClassName");
})
// run it all again
disappear();
});
}
// start it
disappear();
})
Working demo: http://jsfiddle.net/jfriend00/hTmHL/

Categories

Resources