Screwing Around With Javascript and Traffic Lights

Posted by fakebenjay on April 3, 2017

After months of studying Ruby during and before Flatiron’s web dev course, the transition to Javascript has been both enlightening and maddening. With any new language, you develop a sense, over time, of how the langauge works: its scope, its syntax, how it functions, and how to play with it all. But the first few days were rough, and in that time, I’ve developed an irrational hatred of JavaScript inventor and notable homophobe Brendan Eich.

I’m finishing this post several days after I started writing its first sentence (due to a MVC JavaScript project, more on that in an upcoming post), so it’s a little weird to be going back to basic JS concepts after starting React, but here it goes.

One of the best labs in the Learn track for early JavaScript, for me, was the JQuery traffic light (my eventual solution here).

The goal of the lab was to program the behaviors of a standard traffic light: push a button and red light turns red, push another button and the yellow light turns yellow, etc. On top of that, you also have to be able to push some different buttons and a bit of text appears over certain lights that corresponds to that light’s function (red: “STOP”, yellow “whoa hey now”, green “GO”), and most importantly, pushing a final button should enable the green light to flash.

Getting the lights to turn on, and to display messages above them (as well as shutting off the other lights) was fairly simple. All you have to do is set up event listeners for each button that influence the CSS selectors for the various traffic light span classes…

/* code for button 1  -- Turn the light Red */
   $('#button1').click(
     function() {
       clearInterval(colorInterval)
       $('#top-light').css("background", "red");
       $('#middle-light').css("background", "");
       $('#bottom-light').css("background", "");
       $('#middle-light').html("");
       $('#bottom-light').html("");
     }
   );

   /* code for button 2 --- Say STOP */
   $('#button2').click(
     function() {
       clearInterval(colorInterval)
       $('#top-light').html("<p> STOP </p>");
       $('#middle-light').css("background", "");
       $('#bottom-light').css("background", "");
       $('#middle-light').html("");
       $('#bottom-light').html("");
     }
   );

Enabling the basic flashing mechanism was simple enough: set a global variable (yes I know this is a poor practice, but I’m also still a JS n00b) x = 1, set the light’s color to green in a function’s if/else statement when x === 1, make it blank when x === 2, and call that function with a setInterval in a different function. The code below will change the value of x, and with it the color of the light, every 500 miliseconds.

var x;
var a;

function changeColors() {
  return setInterval(change, 500);
}

function change() {
  if (x === 1) {
    color = "green";
    x = 2;
  }
  else if (x === 2) {
    color = "";
    x = 1;
  }
	if (a === 'stop') {
    return;
  }
  $('#bottom-light').css("background", color);
}

//Code for all the other lights

/* code for button 7 -- Make Green Light Blink -- Extension!! */
 +   $('#button7').click(
 +     function() {
 +       a = 'stop'
 +       $('#bottom-light').css("background", "");
 +       $('#top-light').css("background", "");
 +       $('#middle-light').css("background", "");
 +       $('#top-light').html("");
 +       $('#middle-light').html("");
 +       a = ''
 +       changeColors();
 +     }
 +   );

The problem with this code is that it works fine if you push the flashing green button only once, and never press any other button. Pushing the other buttons (whose event listeners all set a = ‘stop’) fail to stop the flashing, while repeated pushes of the flashing green button cause the pattern to become erratic.

Adding the following code to the changeColors function made the flashing stop when other buttons were activated, but did nothing to alleviate the problem of the irregular flash pattern, which would return whenever that button was pressed.

function changeColors() {
  x = 1;
  setInterval(change, 500);

  if (a === 'stop') {
    $('#bottom-light').css("background", "");
    x = 0
    return false
  }
}

Since returning any value in a JavaScript function will cause it to immediately cease, this, in theory, should have stopped the irregular flashes. In reality, however, while this new ‘return false’ in changeColors() hid the flashing light, the return value in change() kept the pattern running in the background, causing a CSS representation of a steady traffic light to have a data leak (as evidenced by console.logs placed in the code.)

The reason the change() function didn’t stop when a === ‘stop’ is that as soon as it returned false, it was started again by the setInterval in changeColors(). Stopping a function works great until it’s called again, and if you set up your code so that a cycle renews constantly, you have to stop that constant renewal.

In this case, that means stopping the setInterval. If you want to un-set an interval, you have to clear the interval. Conveniently, JavaScript has a clearInterval() function.

Setting up the ability to clearInterval() is fairly simple. When you push the flashing green button, set the changeColors() function equal to a variable “colorInterval” and then, with each button press, first call clearInterval(colorInterval) before changing any of the CSS. Doing so effectively stops the setInterval() function, and with it, finally halts the change() function that controls the flashing mechanism.

When you push the flashing green button, repeated pushes don’t screw up the pattern, and pushing other buttons don’t cause it to run in the background indefinitely, resulting in a dynamic and computationally efficient traffic light!

var x;
let colorInterval;

function changeColors() {
  return setInterval(change, 500);
}

function change() {
  if (x === 1) {
    color = "green";
    x = 2;
  }
  else if (x === 2) {
    color = "";
    x = 1;
  }
  $('#bottom-light').css("background", color);
	
	$(document).ready(function() {

  /* code for button 1  -- Turn the light Red */
     $('#button1').click(
       function() {
         clearInterval(colorInterval)
         $('#top-light').css("background", "red");
         $('#middle-light').css("background", "");
         $('#bottom-light').css("background", "");
         $('#middle-light').html("");
         $('#bottom-light').html("");
       }
     );

// All those other lights

   /* code for button 7 -- Make Green Light Blink -- Extension!! */
   $('#button7').click(
     function() {
       x = 1;
       clearInterval(colorInterval)
       $('#bottom-light').css("background", "");
       $('#top-light').css("background", "");
       $('#middle-light').css("background", "");
       $('#top-light').html("");
       $('#middle-light').html("");
       colorInterval = changeColors();
     }
   );

});

}