2

Essentially, I am trying to create a webpage that has 2 buttons; one will cause the screen to flash randomly, and the other will cause it to slowly change colours. I want to be able to switch between these two (if you press the first button it starts flashing and then if you press the second button it slowly changes without any kind of cancel button).

The buttons each call a function which sets the 'running' variable of the other function to false and it's own 'running' variable to true. It then calls a recursive function (recursive in that it just calls itself over and over). These recursive functions only execute their code when their 'running' variable is true.

If you run the snippet you can see that the program is very inconsistent (you may need to play with it for a bit to see the issue since it sometimes seems to work). Sometimes it refuses to change function and other times the two functions seem to both be active and they both try to execute (it almost looks as if they are fighting for control). I don't understand how this is happening since, I believe, only one of the 'running' variables can be true at any time.

var runningDisco = false;
var runningColours = false;


function startColours() {
  if (runningDisco == true); //Is disco running?
  {
    runningDisco = false; //If yes, stop it
  }
  runningColours = true; //Indicate we are running
  window.setTimeout(Colours, 100, 0); //Run
}

function startDisco() {
  if (runningColours == true); {
    runningColours = false;
  }
  runningDisco = true;
  window.setTimeout(Disco, 100);
}

function Disco() {
  if (runningDisco == true); {
    hex = "#";
    for (discoCount = 0; discoCount < 6; discoCount++) {
      hex = hex.concat((Math.floor(Math.random() * 17)).toString(16));
    }
    document.body.style.background = hex;
    window.setTimeout(Disco, 10);
  }

}

function Colours(colourCount) {
  if (runningColours == true); {
    if (colourCount > 359) {
      colourCount -= 359;
    }
    document.body.style.background = "hsl(" + colourCount + ", 50%, 50%)";
    window.setTimeout(Colours, 10, colourCount + 1);
  }

}
input {
  font-family: 'Roboto', sans-serif;
  text-align: center;
  background-color: #dd1021;
  border: none;
  color: white;
  padding: 16px 32px;
  text-decoration: none;
  margin: 4px 2px;
  cursor: pointer;
}
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">

<input id="clickMe" type="button" value="Start Disco" onclick="startDisco();" />
<input id="clickMe" type="button" value="Start Colours" onclick="startColours();" />
KangarooChief
  • 381
  • 3
  • 14

4 Answers4

5

The semicolons after your if-statements are causing a problem

For example, replace:

 if (runningColours == true); {
 if (runningDisco == true); {

with

 if (runningColours == true) {
 if (runningDisco == true) {

Here it is fixed in jsfiddle.

Yosef Weiner
  • 5,432
  • 1
  • 24
  • 37
2
  1. You have semicolons in the wrong place
  2. If you need to run a single animation you need to use a single global variable to hold the timeout handler and clear it when starting a new one.

Like this:

var running;

function startColours() {
  clearTimeout(running);
  running = setTimeout(Colours, 100, 0); //Run
}

function startDisco() {
  clearTimeout(running);
  running = setTimeout(Disco, 100);
}

function Disco() {
    var hex = "#";
    for (discoCount = 0; discoCount < 6; discoCount++) {
      hex = hex.concat((Math.floor(Math.random() * 17)).toString(16));
    }
    document.body.style.background = hex;
    running = setTimeout(Disco, 10);

}

function Colours(colourCount) {
    if (colourCount > 359) {
      colourCount -= 359;
    }
    document.body.style.background = "hsl(" + colourCount + ", 50%, 50%)";
    running = setTimeout(Colours, 10, colourCount + 1);
}
input {
  font-family: 'Roboto', sans-serif;
  text-align: center;
  background-color: #dd1021;
  border: none;
  color: white;
  padding: 16px 32px;
  text-decoration: none;
  margin: 4px 2px;
  cursor: pointer;
}
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">

<input id="clickMe" type="button" value="Start Disco" onclick="startDisco();" />
<input id="clickMe" type="button" value="Start Colours" onclick="startColours();" />
mplungjan
  • 169,008
  • 28
  • 173
  • 236
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • It's a nice suggestion but has nothing to do with the problem the OP was asking about – Yosef Weiner May 11 '17 at 08:56
  • 1
    @SkinnyJ Not really. Original problem OP is trying to solve was "... I want to be able to switch between these two". His code is XY-problem. And I provide a better solution to the original problem. Otherwise this question should be closed as it was just a typo. – Yury Tarabanko May 11 '17 at 09:00
  • @mplungjan True. Removed. – Yury Tarabanko May 11 '17 at 09:04
0

You made a common mistake to those new to JavaScript - including a semi-colon after you if header. This creates an if statement with no body that is followed by a block containing the single statement runningColours = false, which will not execute correctly. The below should fix this:

if (runningColours == true)
{
    runningColours = false;
}

The following JSFiddle link should have a working version of your script: https://jsfiddle.net/4qhmtnhe/ I hope it starts working, and you continue to learn JavaScript.

0

The real problem is the semicolons after the if statements inside the Disco and Colours functions:

if (runningDisco == true); {
  document.body.style.background = …
}

will execute as

if (runningDisco == true)
  ;
{
  document.body.style.background = …
}

which runs the content regardless of the boolean variable. Why does it work at all sometimes? I guess when the two setTimeout callbacks run close enough to each other that only the latter is rendered at all it feels consistent. Of course that is very unstable - they are indeed fighting each other for screen time.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375