Traffic Light 🚦

JavaScript Easy 50 minSpotifyUber

Prompt

Create a traffic light using only HTML, CSS, and Vanilla JavaScript, featuring three distinct colors: red 🔴, yellow 🟡, and green 🟢.

Requirements

  • A button that randomly turns on one of the lights.
  • A button that continuously cycles through lights in sequence (red → yellow → green → red) with a 3 second interval.
  • A button that stops the light from turning on.

Let's see your traffic light in action! 🚦

Example

Playground

Important: When adding functions to index.js, you must attach them to the window object (e.g., window.functionName = function() {...}). This is because the StackBlitz playground uses JavaScript modules, which don't automatically expose functions globally. Your HTML onclick handlers need these functions to be globally accessible. This is only applicable to the Vanilla JavaScript questions.

Hint 1

For styling the traffic light, consider using border-radius: 50% to create circular lights. You can represent the "off" state of a light by using a darker shade of the color (e.g. #4a4a4a) or a gray color. When a light is "on", change its background color to the appropriate bright color (red (e.g. #e5484d), yellow (e.g. #ffe629), or green (e.g. #30a46c)).

Hint 2

To randomly select and turn on one of the lights, you'll need to use Math.random() in combination with Math.floor() to generate a random index (0, 1, or 2) that corresponds to one of the three lights. Before turning on a new light, make sure to turn off any previously activated light. This can be done by removing a CSS class from all lights and then adding it only to the selected light.

Hint 3

For the continuous light change feature, you'll need to use setInterval() to repeatedly call your random light function. Store the interval ID in a variable so you can later use it with clearInterval() when the stop button is clicked. Make sure to check if an interval is already running before starting a new one to prevent multiple intervals from running simultaneously.

Solution

Explanation

HTML Structure

The HTML creates the visual structure of our traffic light and control buttons:

<div class="container">
<div class="traffic-light">
<div class="light red" id="red"></div>
<div class="light yellow" id="yellow"></div>
<div class="light green" id="green"></div>
</div>
<div class="controls">
<button onclick="randomLight()" class="control">Random</button>
<button onclick="startCycling()" class="control">Cycle Lights</button>
<button onclick="stopTrafficLight()" class="control">Stop</button>
</div>
</div>

Each light is a div with two classes: a general light class (for shared styling) and a specific color class (red, yellow, or green). We also give each light an id matching its color so we can easily target it with JavaScript.

The buttons have onclick attributes that call specific JavaScript functions when clicked. This is how we connect user interactions to our code.

CSS Styling

The CSS handles the visual appearance of our traffic light:

.traffic-light {
background-color: var(--frame-color);
padding: 16px;
border-radius: 12px;
display: flex;
flex-direction: column;
gap: 16px;
}

.light {
width: 80px;
height: 80px;
border-radius: 50%;
background-color: var(--light-off);
position: relative;
transition: background-color 0.3s ease;
will-change: background-color;
}

Notice how we use border-radius: 50% to create perfectly circular lights. By default, all lights have the "off" color defined by the --light-off CSS variable.

The magic happens with these CSS classes:

.light.active.red {
background-color: var(--light-red);
}

.light.active.yellow {
background-color: var(--light-yellow);
}

.light.active.green {
background-color: var(--light-green);
}

When a light has both its color class (e.g., red) and the active class, it changes from the "off" color to its bright color. This is how we turn lights on and off without changing the HTML structure.

We also added a smooth transition with transition: background-color 0.3s ease, which creates a gentle fade effect when lights change state.

JavaScript Functionality

Now let's look at how the JavaScript brings our traffic light to life:

// Global variables
let intervalId = null;
const LIGHTS = ['red', 'yellow', 'green'];
let currentLightIndex = 0;

We start by defining some important variables:

  • intervalId will store the ID of our interval when cycling lights
  • LIGHTS is an array of our light colors, which makes our code more maintainable
  • currentLightIndex keeps track of which light is currently active in the sequence

Random Light Function

window.randomLight = function () {
// Turn off all lights
clearAllLights();

// Turn on a random light
const randomIndex = Math.floor(Math.random() * LIGHTS.length);
const selectedLight = document.getElementById(LIGHTS[randomIndex]);
selectedLight.classList.add('active');
};

This function does two things:

  1. Calls clearAllLights() to turn off any currently active lights
  2. Selects a random light and turns it on by adding the active class
  3. To select a random light, we use Math.random() which gives us a decimal between 0 and 1. We multiply by the number of lights (3) and use Math.floor() to round down to a whole number (0, 1, or 2). This gives us a random index into our LIGHTS array.

Cycling Lights

window.startCycling = function () {
// Don't start a new interval if one is already running
if (intervalId !== null) {
stopTrafficLight();
}

// Reset to start with red light
currentLightIndex = 0;

// Start cycling lights in sequence
cycleLights();
intervalId = setInterval(cycleLights, 3000);
};

window.cycleLights = function () {
// Turn off all lights
clearAllLights();

// Turn on the current light in sequence
const selectedLight = document.getElementById(LIGHTS[currentLightIndex]);
selectedLight.classList.add('active');

// Move to next light (cycle back to red after green)
currentLightIndex = (currentLightIndex + 1) % LIGHTS.length;
};

The startCycling function:

  1. Checks if an interval is already running and stops it if needed
  2. Resets the light sequence to start with red
  3. Immediately calls cycleLights() to show the first light
  4. Sets up an interval to call cycleLights() every 3 seconds

The cycleLights function:

  1. Turns off all lights
  2. Activates the current light in the sequence
  3. Updates currentLightIndex to point to the next light

The clever part is how we update the index: currentLightIndex = (currentLightIndex + 1) % LIGHTS.length. This uses the modulo operator (%) to cycle back to 0 after reaching the end of the array. So the sequence goes 🔴 → 🟡 → 🟢 → 🔴 → 🟡 → 🟢, and so on.

Stopping the Traffic Light

window.stopTrafficLight = function () {
// Clear the interval
if (intervalId !== null) {
clearInterval(intervalId);
intervalId = null;
}

// Turn off all lights
clearAllLights();
};

This function:

  1. Checks if an interval is running
  2. If so, cancels it with clearInterval and resets intervalId
  3. Turns off all lights

Helper Function

function clearAllLights() {
LIGHTS.forEach((color) => {
document.getElementById(color).classList.remove('active');
});
}

This helper function loops through all lights and removes the active class, effectively turning them all off. We use this before activating a new light to ensure only one light is on at a time.

Putting It All Together

When a user clicks a button, the corresponding JavaScript function is called:

  1. Random Button: Turns off all lights and activates a random one
  2. Cycle Lights Button: Starts a sequence that changes lights every 3 seconds in the traffic light order (red → yellow → green → red)
  3. Stop Button: Cancels any running interval and turns off all lights

The JavaScript manipulates the DOM by adding or removing the active class, and the CSS responds by changing the background color of the lights. This separation of concerns (HTML for structure, CSS for appearance, JavaScript for behavior) creates clean, maintainable code.

The use of setInterval and clearInterval allows us to create a repeating action (changing lights) and stop it when needed. By storing the interval ID, we ensure we don't create multiple intervals that would cause unpredictable behavior.

This traffic light is a perfect example of how these three technologies work together to create an interactive web application!

Common Pitfalls

  • Forgetting to Clear Previous Lights: When turning on a new light in the sequence, make sure you're first turning off any previously active light. Failing to do this will result in multiple lights being on simultaneously.
  • Multiple Intervals Running: When implementing the continuous light cycling feature, a common error is starting multiple intervals without clearing previous ones. This can lead to unpredictable behavior where lights change at inconsistent times. Always store your interval ID and check if an interval is already running before starting a new one.
  • Incorrect Cycling Order: Make sure to maintain the correct traffic light sequence (red → yellow → green → red) by properly incrementing and resetting the index counter.

Interviewer Criteria

HTML/CSS

  • Did I create a visually appealing traffic light using CSS with clear styling for active and inactive states?

  • Have I made good use of CSS variables for colors and maintained consistent naming conventions?

  • Did I implement smooth transitions when lights change state using CSS transitions?

JavaScript

  • Did I implement a clean function to randomly select and activate one of the lights?

  • Have I properly handled the continuous light change using setInterval and clearInterval?

  • Did I ensure only one interval can run at a time to prevent multiple timers from running simultaneously?

  • Have I written a function to clear all active lights before activating a new one?

  • Is my JavaScript code well-organized with meaningful function and variable names?

  • Did I properly connect DOM elements to JavaScript functionality?

Code Structure and Performance

  • Have I organized my code with clear separation of concerns between structure (HTML), presentation (CSS), and behavior (JavaScript)?

  • Is my code DRY (Don't Repeat Yourself) with reusable functions and minimal duplication?

  • Have I handled potential edge cases, such as rapidly clicking buttons or refreshing the page?

Time Checkpoints

  • 10:00 AM

    Interview starts 👥

  • 10:03 AM

    Prompt given by the interviewer

  • 10:05 AM

    Candidate reads the prompt, asks clarifying questions, and starts coding

  • 10:10 AM

    HTML structure of traffic light and buttons created

  • 10:20 AM

    CSS styling of traffic light completed

  • 10:25 AM

    Button styling completed

  • 10:30 AM

    Function to randomly activate a light implemented

  • 10:35 AM

    Logic to clear active lights before activating a new one implemented

  • 10:40 AM

    Cycle lights functionality implemented

  • 10:45 AM

    Stop functionality using clearInterval implemented

  • 10:50 AM

    Testing and debugging the application

  • 11:00 AM

    Interview ends ✅

00:00