javascript change background and play sound with one button - javascript

I'm building a clock inspired from the link below with added features like the single party button will play a music and change the BodyBGcolor per second.
https://codepen.io/codifiedconcepts/pen/bwgxRq
The music plays and pauses fine, but the BG starts changing but doesn't stop if I click to pause, and starts flickering and gets faster if I click the button again.
var partyTime = false;
var catMusic = new Audio("media/party.mp3");
function partyEvent() {
// partyTime and catMusic are defined outside
// otherwise they get re-defined without changing the old definition
if (partyTime === false) {
partyTime = true;
catMusic.play();
partyBtn.innerHTML = "PARTY OVER";
} else {
partyTime = false;
catMusic.pause();
partyBtn.innerHTML = "PARTY TIME";
}
}
var i = 0;
function changeBG() {
var color = ["red", "blue", "brown", "green"];
document.body.style.backgroundColor = color[i];
i = (i + 1) % color.length;
if (partyTime === true) {
var initBG = setInterval(changeBG, 1000);
} else {
clearInterval(initBG);
}
}
I have another function which changes the BGcolor 5 times in a day, for morning, noon, evening etc.. as a switch statement, I'm showing one case here..
switch (true) {
case hour <= 5:
imgTxt.innerHTML = "GET SOME SLEEPZ BRO";
catImg.style.background = "url('img/earlyMorning.jpg')";
catImg.style.backgroundSize = "cover";
document.body.style.backgroundColor= "#2d3037";
clockBody.style.color = "#99ffcc";
break;
please help this noob, and sorry for the indentation, it gets all same after pasting here..

if (partyTime === true) {
var initBG = setInterval(changeBG, 1000);
} else {
clearInterval(initBG);
}
Here you are creating initBG local to if block. In the else block initBG is undefined so it does not stop the interval process.
Solution: Define initBG outside the partyEvent function.
var initBG;
if (partyTime === true) {
initBG = setInterval(changeBG, 1000);
} else {
clearInterval(initBG);
}

Related

JS: setInterval running as if there were multiple intervals set

So, I've been trying to figure out JS, and what better way to do so than to make a small project. It's a small trivia game, and it has a question timer I've made using setInterval. Unfortunately, after answering multiple questions, the interval's behaviour gets very weird - it runs the command twice every time. I guess it's my faulty implementation of buttonclicks?
By the way, if my code is awful I am sorry, I've been desperate to fix the issue and messed with it a lot.
function startGame(){
if (clicked === true){
return;
}
else{
$("#textPresented").html("Which anthem is this?");
$("#button").css("display", "none");
currentCountry = getRndInteger(0,8);
console.log(currentCountry);
var generatedURL = anthemnflags[currentCountry];
console.log(generatedURL);
audios.setAttribute("src", generatedURL);
audios.play();
$("#button").html("I know!");
$("#button").css("display", "block");
$("#button").click(function () {
continueManager();
});
y=10;
console.log("cleared y" + y);
x = setInterval(function(){
y = y - 1;
console.log("Counting down..." + y)
}, 1000);
console.log("INTERVAL SET");
}
}
Here is the console output:
cleared y10 flaggame.js:59:17
INTERVAL SET flaggame.js:64:17
AbortError: The fetching process for the media resource was aborted by the user agent at the user's request. flaggame.js:49
Counting down...9 flaggame.js:62:20 ---- THESE TWO ARE BEING PRINTED AT THE SAME TIME
Counting down...8 flaggame.js:62:20 ---- THESE TWO ARE BEING PRINTED AT THE SAME TIME
Counting down...7 flaggame.js:62:20
Counting down...6 flaggame.js:62:20
Counting down...5 flaggame.js:62:20
Counting down...4 flaggame.js:62:20
Counting down...3 flaggame.js:62:20
Counting down...2 flaggame.js:62:20
Counting down...1 flaggame.js:62:20
Counting down...0
THE REST OF MY CODE:
function middleGame(){
$("#button").css("display", "none");
var n = document.querySelectorAll(".flagc").length;
correctIMG = getRndInteger(0,n-1);
showFlags();
var taken = new Array();
for (var i = 0; i < n; ++i){
if (i === correctIMG){
images[i].attr("src", "res/" + flagsfiles[currentCountry]);
taken[currentCountry] = true;
}
else{
var randomFlag = getRndInteger(0, flagsfiles.length);
if (randomFlag !== currentCountry && taken[randomFlag] !== true){
images[i].attr("src", "res/" + flagsfiles[randomFlag]);
taken[randomFlag] = true;
}
}
}
$(".flagc").click(function(){
clickregister(this);
});
}
function continueManager(){
if (!clicked){
audios.pause()
clearInterval(x);
x = 0;
clicked = true;
middleGame();
return;
}
}
function clickregister(buttonClicked){
if ($(buttonClicked).attr("id") != correctIMG){
points = points - 1;
flagARR[$(buttonClicked).attr("id")].css("display", "none");
console.log("INCORRECT");
}
else{
if (y >= 0) {
var addedPoints = 1 + y;
points = points + addedPoints;
$("#points").html(points);
}
else{
points = points + 1;
}
hideFlags();
clicked = false;
startGame();
}
}
$(function(){
hideFlags();
$("#textPresented").html("When you're ready, click the button below!");
$("#button").html("I am ready!");
$("#button").click(function () {
if (!gameStarted){
gameStarted = true;
alert("STARTING GAME");
startGame();
}
});
});
Basically this is how it works:
When the "I am ready" button is clicked, startGame() is called. It plays a random tune and counts down, until the player hits the "I know" button. That button SHOULD stop the interval and start the middleGame() function, which shows 4 images, generates a random correct image and awaits input, checks if it's true, then launches startGame() again.
The first and second cycles are perfect - after the third one things get messy.
I also noticed that the "INCORRECT" log gets printed twice, why?
EDIT: here is the minimized code that has the same issue:
var x;
var gameStarted = false;
var y;
var clicked;
$(function(){
$("#button").click(function () {
if (!gameStarted){
gameStarted = true;
startGame();
}
});
});
function startGame(){
console.log("startgame()");
if (clicked === true){
return;
}
else{
console.log("!true");
$("#button").css("display", "block");
$("#button").click(function () {
continueManager();
});
y=10;
x = setInterval(function(){
y = y - 1;
console.log(y);
}, 1000);
}
}
function continueManager(){
if (!clicked){
clearInterval(x);
x = 0;
clicked = true;
middleGame();
return;
}
}
function middleGame(){
$("#button").css("display", "none");
var taken = new Array();
$(".flagc").click(function(){
clickregister(this);
});
}
function clickregister(buttonClicked){
console.log("clickgregister");
//Irrelevant code that checks the answers
clicked = false;
startGame();
}
EDIT2: It appears that my clickregister() function gets called twice, and that function then calls startGame() twice.
EDIT3: I have found the culprit! It's these lines of code:
$(".flagc").click(function(){
console.log("button" + $(this).attr("id") + "is clicked");
clickregister(this);
});
They get called twice, for the same button
I fixed it!
It turns out all I had to do was to add
$(".flagc").unbind('click');
Before the .click() function!
You need to clear the interval first then call it again. You can do that by creating a variable outside of the event listener scope and in the event listener check if the variable contains anything if yes then clear the interval of x. After clearing the interval you can reset it.
Something like this:
<button class="first" type="submit">Button</button>
const btn = document.querySelector('.first');
let x;
btn.addEventListener("click", () => {
x && clearInterval(x)
x = setInterval(() => console.log("yoo"), 500)
})
This is because if you don't clear the interval of x it will create a new one on every button press.

First button click, change colour, Second click change font family

I have a simple HTML with a paragraph and button. I want to carry out different commands on every click of the button. For first click, change paragraph colour, on second click change font family, on third click copy the innerHTML 5 times(pls explain how to do this), on four or more clicks, change innerHTML to "DONE".
var clicks = 0;
var a = document.getElementById("output");
function go() {
clicks ++;
moves();
};
function moves() {
if (clicks = 1) {
a.style.color = "red";
}
else if (clicks = 2) {
a.style.fontFamily = "sans-serif";
}
}
<html>
<head>
<title></title>
</head>
<body>
<button onclick=go();>ClickMe</button>
<p id="output">Change Me</p>
</body>
<script src="q2.js" type="text/javascript"></script>
</html>
you have used = instead of ==.= are used for assignment and for compare you must use == or ===.
also you don't need to move function and I moved it to go function
var clicks = 0;
var a = document.getElementById("output");
function go() {
clicks ++;
if (clicks == 1) {
a.style.color = "red";
}
if (clicks == 2) {
a.style.fontFamily = "sans-serif";
}
if (clicks == 3) {
a.innerText = a.innerText+a.innerText+a.innerText+a.innerText;
}
if (clicks == 4) {
a.innerText = "done";
}
};
<body>
<button onclick=go();>ClickMe</button>
<p id="output">Change Me</p>
</body>
You can use something like an array of functions. See the comments for explanation under every function.
$(function () {
// Let's start with an array of functions that needs to be executed.
var fns = [function () {
// First one.
// Change Para Colour.
$("#output").css("color", "#00f");
}, function () {
// Second one.
// Change Para Font.
$("#output").css("font-family", "monospace");
}, function () {
// Third one.
// Copy the inner HTML 5 times. You can use something like String.repeat().
$("#output").html($("#output").html().repeat(5));
}, function () {
// Fourth one.
// Done.
$("#output").html("Done.");
}];
var counter = 0;
$("button").click(function () {
if (counter >= 4)
return false;
fns[counter]();
counter++;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>ClickMe</button>
<p id="output">Change Me</p>
Use repeated to make 5 copies and store it in a variable.
const content = document.getElementById('content');
let copy5;
let count = 0;
content.addEventListener('click', function(e) {
count += 1;
if (count === 1) {
content.style.color = 'green';
}
if (count === 2) {
content.style.fontFamily = "Arial, Helvetica, sans-serif"
}
if (count === 3) {
copy5 = content.innerText.repeat(5);
console.log(copy5)
}
if (count === 4) {
content.innerHTML = 'DONE';
count = 0;
}
})
<div id="content">I have a simple HTML with a paragraph and button. I want to carry out different commands on every click of the button. For first click, change paragraph colour, on second click change font family, on third click copy the innerHTML 5 times(pls explain
how to do this), on four or more clicks, change innerHTML to "DONE".</div>
You can create a queue - implemented by a simple array - of functions like below (just a simplified example which you can base on):
var changeColor = function() {
a.style.color = "red";
};
var changeFont = function() {
a.style.fontFamily = "sans-serif";
};
var moves = [changeColor, changeColor, changeFont, changeFont, changeFont];
function go() {
var move = moves.shift(); // get and remove the first move from the queue
if (move) move();
}
The idea is storing the moves in that queue in the order you want to execute them. So, if you want a certain function to be executed 4 times, you put it 4 times in the queue, followed by the next function in the amount you want, and so on and so forth.
The shift function gets the next move to be executed. You need to call only that function, go, to execute the next move.
With this approach, you don't need to count the number of clicks.
as far i understood from your question, the below code can work fine for you... in your question it said "on third click copy the innerHTML 5 times", loop the same function for the 5 times.
var count = 0;
function go(){
count++;
switch(count){
case 1: document.getElementById('output').style.color = 'green'; break;
case 2: document.getElementById('output').style.fontFamily = 'sans-serif'; break;
case 3: copyText('output'); break;
case 4: document.getElementById('output').innerHTML = '"DONE"';
}
}
function copyText(containerid){
if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(document.getElementById(containerid));
range.select().createTextRange();
document.execCommand("Copy");
} else if (window.getSelection) {
var range = document.createRange();
range.selectNode(document.getElementById(containerid));
window.getSelection().addRange(range);
document.execCommand("Copy");
}
}
You've got the right idea, but you are missing the actual click event handling registration.
Also, it's cleaner and easier to maintain if you add/remove CSS classes, rather than modifying individual styles.
Lastly, because of the particular if logic you have, a switch statement is easier to read and slightly better in performance.
var clicks = 1;
var o = document.getElementById("output");
var b = document.getElementById("btn");
// Store original text
var original = o.textContent;
// Set up a click event handler:
b.addEventListener("click", moves);
function moves() {
switch (clicks) {
case 1:
o.classList.add("one");
break;
case 2:
o.classList.add("two");
o.classList.remove("one");
o.textContent = repeatText(5, o.textContent);
break;
case 3:
o.classList.add("three");
o.classList.remove("two");
o.textContent = original;
break;
case 4:
// Intentional fall-through to the default
default:
o.classList.remove("three");
o.textContent = "Done";
break;
}
// Can't have a space before or after ++
clicks++;
}
function repeatText(numTimes, data){
var result = "";
for(var i = 0; i < numTimes; i++){
result += data;
}
return result;
}
.one {
color:red;
}
.two {
font-family:sans-serif;
}
.three {
font-weight:bold;
}
<button id="btn">ClickMe</button>
<p id="output">Change Me</p>

Can't get a jquery 'else if' to work

So here's the code snippet
$(document).ready(function () {
var dmg = 60;
var curr_hp = 1200;
var tot_hp = 1200;
$('#attk_spd').text('1.2 seconds');
$('#dmg').text('60');
$('#curr_hp').text('1200');
$('#tot_hp').text('1200');
var dps_timer;
var attacking = 0;
$("#btn").click(function () {
if (dps_timer) {
// We're already waiting for the next attack, so do nothing
return;
}
$('#attk').text('You are currently attacking your target.');
$('#btn').val('Retreat');
var attack_once = function () {
// Always attack immediately
curr_hp -= dmg;
$('#curr_hp').text(curr_hp);
if (curr_hp > 0) {
dps_timer = setTimeout(attack_once, 1200);
} else if (attacking == 1) {
clearTimeout(attack_once);
attacking = 0;
alert(attacking);
$('#attk').text('You have retreated from battle.');
$('#btn').val('Attack');
} else {
attacking = 0;
alert(attacking);
$('#attk').text('Your target has been destroyed.');
$('#btn').val('Attack');
}
}
if (attacking == 0) {
attack_once();
attacking = 1;
alert(attacking);
}
});
});
I have alerts set up to check that the values are changing. This is basically a button click function. So when i click the button the first time the attacking starts and the button changes to retreat. However when i click retreat, the function should be executed again checking the conditionals, the attacking variable is set to 0 initially, changes to 1 when you click attack. So that if you click attack again it will run the second 'else if' conditional, but it doesn't. Help!
Code in action right here: http://www.evenstar-online.com/Jquery_snippets/dpsloop.php
attacking can never be 1 when attack_once is called because your if statement requires it to be 0 to run.
if (attacking == 0) {
//attacking is 0 when `attack_once()` is called
attack_once();
attacking = 1;
alert(attacking);
}
var attack_once = function () {
//.....
// this can never be 1
} else if (attacking == 1) {
Solution: Move your attacking = 1 up above the attack_once(); line
if (attacking == 0) {
attacking = 1;
attack_once();
alert(attacking);
}

Stop awaiting input after fixed time

I'm new to web programming and I stumbled upon a problem that I couldn't solve and I'm not sure it can be solved. I'm creating a very simple "game" using jquery, and I want to make the thread to stop waiting for the (keydown) input and just carry on with the code, so I can perform either a simple upwards "jump", or a " left jump"/"right jump". Can it be done?
Here follows the codebit from what I've been doing so far:
http://www.codecademy.com/pt/pySlayer10761/codebits/OYQ11a/edit
You need a game loop thats running independantly from your keydown-handler. Elsewise any animation you might hack into the keydown handler might stop the moment no inputs are made anymore.
By looking at your code, I can see you tried to do it by creating a new setTimeout() on those keydowns. You are creating this for every keydown event fired. This is very likely to crash/freeze your browser at some point if the engine does not realize you are creation the same timeout over and over again.
Do it like this: in the onkeydown handler you only set a variable keydowncode to the keycode value. Then you create a new game loop like this
<script>
var keydownCode = 0;
var isInAnimation = false;
var animatedKeydownCode = 0;
var animationStartTime = 0;
var animationStartValue = 0;
// Lightweight keydown handler:
$(document).keydown(function(key) {
keydownCode = parseInt(key.which,10);
}
$(document).keyup(function(key) {
keydownCode = 0;
}
function animation() {
// here comes your animation logic..
// e.g. keep moving for 100 to 200 milliseconds after keypress
// Milli secs difference from
var nowTime = Date.now();
// Animations get prioritized: Only one animation at the same time!
if(isInAnimation) {
switch(animatedKeydownCode) {
case 37:
var delta = (nowTime-animationStartTime)/100*10;
if(delta > 10) { delta = 10; isInAnimation = false; }; // Animation over!
$('img').left(animationStartValue-delta);
case 37:
var delta = (nowTime-animationStartTime)/200*10;
if(delta > 10) { delta = 10; isInAnimation = false; }; // Animation over!
$('img').top(animationStartValue-delta);
case 39:
var delta = (nowTime-animationStartTime)/100*10;
if(delta > 10) { delta = 10; isInAnimation = false; }; // Animation over!
$('img').left(animationStartValue+delta);
}
// Ready to take new input, if its not outdated
} else {
// If some key is down and no animations active
if(keydownCode > 0) {
switch(keydownCode) {
case 37:
animationStartTime = nowTime;
animatedKeydownCode = keydownCode;
animationStartValue = $('img').left();
isInAnimation = true;
case 38:
// Only start a jump if on bottom
if($('img').css("top") == '390px') {
animationStartTime = nowTime;
animatedKeydownCode = keydownCode;
animationStartValue = $('img').top();
isInAnimation = true;
}
case 39:
animationStartTime = nowTime;
animatedKeydownCode = keydownCode;
animationStartValue = $('img').left();
isInAnimation = true;
}
}
}
window.requestAnimationFrame(animation);
}
window.requestAnimationFrame(animation);
</script>
This is no full game, you need to adjust it by yourself to get a working game. E.g. you might want to add some gravity to get mario down again when there is no input..
I think you are looking for setTimeout().
Like this:
setTimeout(function(){
// do something here which will be executed after 5 seconds.
},5000);
You can't stop a thread in javascript as it is single threaded.

Why does my javascript countdown timer occasionally over-run?

As part of a web-based educational game, I have a countdown timer using the code that follows. Mostly, the timer stops at zero, but occasionally, it over-runs, and continues to count down from 59:59.
A couple of notes about the code:
countTime is a global variable set by a dropdown menu
stopCount is a gloabal variable set by a reset button
leadZero is an external function used for formatting
I understand that setTimeout is not very accurate, but I would have thought that the remTime > 0 condition would stop the recursion eventually, even if it missed the first time.
Anyway, here's the code:
function newCount() {
var startTime=new Date();
var min=startTime.getMinutes();
var sec=startTime.getSeconds();
var endTime=(min*60)+sec+countTime;
stopCount=false;
countDown();
function countDown() {
var today=new Date();
var m=today.getMinutes();
var s=today.getSeconds();
var currTime=(m*60)+s;
var remTime=endTime-currTime;
var remMin = Math.floor(remTime/60);
var remSec = remTime % 60;
// add a zero in front of numbers<10
remMin=leadZero(remMin);
remSec=leadZero(remSec);
document.getElementById('timer').innerHTML=remMin+":"+remSec;
if (remTime > 0 && stopCount==false) {
t=setTimeout(function(){countDown()},500);
}
else if (stopCount==false){document.getElementById("nextButton").innerHTML = "Finished";}
else {}
}
}
As requested, here is the code for the buttons and calling functions ...
Buttons:
<button onclick="newSyllable()" id="nextButton" style="font:60px 'Nunito', sans-serif;">Start</button>
<button onclick="resetScore()"><span style="font:20px 'Nunito', sans-serif;">Reset</span></button>
Functions:
function resetScore() {
points=0
stopCount=true;
document.getElementById("score").innerHTML = "Score: " + points
document.getElementById("nextButton").innerHTML = "Start"
document.getElementById("syllable").innerHTML = " "
t=setTimeout(function(){setCountDown()},500);
}
function newSyllable() {
if (document.getElementById("nextButton").innerHTML == "Finished"){
}
else {
if (document.getElementById("nextButton").innerHTML == "Start"){
newCount();
}
document.getElementById("nextButton").innerHTML = "Next"
switch (currentUnit) {
case "1":
unit1();
break;
case "2":
unit2();
break;
case "3":
unit3();
break;
case "4":
unit4();
break;
case "5":
unit5();
break;
case "6":
unit6();
break;
}
document.getElementById("score").innerHTML = "Score: " + points++
}
}
Ok so I think the problem originates from the way you're calculating the remaining time by using two Date objects. A much simpler way to do this would be to just use your countTime variable as the starting time (in seconds), and then use a 1000 millisecond interval to perform the countdown. Try this code instead:
var stopCount = false;
var countTime = 10;
function newCount() {
if(stopCount === false) {
var counter=setInterval(countDown, 1000);
}
stopCount = true;
function countDown() {
countTime = countTime - 1;
var remMin = Math.floor(countTime/60);
var remSec = countTime % 60;
// add a zero in front of numbers<10
remMin=leadZero(remMin);
remSec=leadZero(remSec);
document.getElementById('timer').innerHTML=remMin+":"+remSec;
if (countTime <= 0)
{
clearInterval(counter);
document.getElementById("nextButton").innerHTML = "Finished";
return;
}
}
}
JSFiddle: http://jsfiddle.net/5vxbe/8/
I simplified the code further, using the countTime = countTime-1 idea from #JL, but using my original setTimeout recursive function rather than setInterval and clearInterval.
I want the "reset" button to be able to stop and reset the timer before it's finished, and this code made that a bit simpler (I think).
Here's the final code:
function newCount() {
stopCount=false;
countDown();
function countDown() {
if (stopCount==false) {
countTime=countTime-1
var remMin = Math.floor(countTime/60);
var remSec = countTime % 60;
// add a zero in front of numbers<10
remMin=leadZero(remMin);
remSec=leadZero(remSec);
document.getElementById('timer').innerHTML=remMin+":"+remSec;
if (countTime > 0) {
t=setTimeout(function(){countDown()},1000);
}
else {document.getElementById("nextButton").innerHTML = "Finished";}
}
}
}

Categories

Resources