INF-250: Web Design and Development — JavaScript Page Manipulation and User Input
Last Class Recap
Last class we covered how JavaScript connects to the DOM. We learned how to select elements and modify them — changing text, styles, and attributes. We then applied those ideas in the text editor exercise. Here is the core of what we had working:
const text = document.getElementById("mainText");
let currentSize = 32;
function changeColorToRed() {
text.style.color = "red";
}
function changeSize() {
currentSize += 5;
text.style.fontSize = currentSize + "px";
}
function toggleBold() {
if (text.style.fontWeight === "bold") {
text.style.fontWeight = "normal";
} else {
text.style.fontWeight = "bold";
}
}One of the challenges hinted at in the file was: cycle through colors. Today we are going to build that feature step by step.
Building the Color Cycler: Step by Step
Before writing any code, let's think about what we actually need. This is called pseudo-code — writing out the logic in plain language before translating it into code.
- We have a list of colors, a button, and some text.
- When the button is clicked, take the first entry from the color list and make the text that color.
- When the button is clicked again, go to the next item.
- When the end of the list is reached, go back to the start.
That tells us some important things: a list of colors and a way to remember where we are in that list.
We also need to know the length of the list so we know when to loop back.
Step 1 — Store the colors in an array
An array is the right tool here. We want an ordered list of values we can move through one at a time.
const colors = ["red", "blue", "green", "purple", "orange"];
console.log(colors); // the full array
console.log(colors[0]); // "red" — index 0
console.log(colors[1]); // "blue" — index 1
console.log(colors.length); // 5Step 2 — Add a variable to track our position
We need to know which color we are currently on. We will use a variable called colorIndex and start it at 0 (the first item in the array).
const colors = ["red", "blue", "green", "purple", "orange"];
let colorIndex = 0;
console.log("starting index:", colorIndex);
console.log("color at that index:", colors[colorIndex]); // "red"
colorIndex++;
console.log("after one click:", colorIndex);
console.log("color at that index:", colors[colorIndex]); // "blue"colorIndex is declared with let, not const. That is because its value needs to change every time the button is clicked. Variables declared with const cannot be reassigned. Step 3 — Write a first draft of the function
On each click, we want to advance the index by one and then apply the color at that new index.
const colors = ["red", "blue", "green", "purple", "orange"];
let colorIndex = 0;
function cycleColor() {
colorIndex++;
text.style.color = colors[colorIndex];
} Try clicking the button five times in CodePen — watch the console to see what is happening to colorIndex. What goes wrong after the fifth click?
Step 4 — Understand the problem: going out of bounds
Arrays in JavaScript are zero-indexed. An array with 3 items has valid indexes of 0, 1, and 2. If you try to access index 3, JavaScript does not throw an error — it returns undefined.
In our function, once colorIndex reaches 5, colors[5] is undefined. Setting text.style.color = undefined does nothing — the color just stops changing. We need to reset the index before that happens.
Step 5 — Wrap the index back to zero
After incrementing, we check whether the index has gone past the last item. If it has, we reset it to 0.
if (colorIndex >= colors.length) {
colorIndex = 0;
} The condition colorIndex >= colors.length is more reliable than colorIndex === colors.length — it catches the case where the index somehow jumps ahead by more than one.
A Cleaner Way: The Modulo Operator
The % operator is called modulo. It asks: how many whole times does the right number fit into the left number, and what is left over? That leftover is the result.

For example, 7 % 5: 5 fits into 7 one whole time (1 × 5 = 5), with 2 left over — so the result is 2. And 2 % 5: 5 fits into 2 zero whole times, with 2 left over — so the result is also 2. It is not the same as 2 / 5 (which gives 0.4). Modulo only counts whole fits.
// % gives you the remainder after fitting the right number into the left number as many times as possible
console.log(0 % 5); // 0 (5 fits into 0 zero times, with 0 left over)
console.log(1 % 5); // 1 (5 fits into 1 zero times, with 1 left over)
console.log(2 % 5); // 2 (5 fits into 2 zero times, with 2 left over)
console.log(3 % 5); // 3 (5 fits into 3 zero times, with 3 left over)
console.log(4 % 5); // 4 (5 fits into 4 zero times, with 4 left over)
console.log(5 % 5); // 0 (5 fits into 5 one time, with 0 left over)
console.log(6 % 5); // 1 (5 fits into 6 one time, with 1 left over)
console.log(7 % 5); // 2 (5 fits into 7 one time, with 2 left over)Notice that the results never reach 5 — they count up to 4 and then reset to 0. That is exactly the wrapping behavior we need for cycling through an array.
Here is a trace of every click in our color cycler, with an array of length 5:
// colors has 5 items — length is 5
const colors = ["red", "blue", "green", "purple", "orange"];
// Watch what (colorIndex + 1) % 5 produces on each click:
// Click 1: (0 + 1) % 5 = 1 → "blue"
// Click 2: (1 + 1) % 5 = 2 → "green"
// Click 3: (2 + 1) % 5 = 3 → "purple"
// Click 4: (3 + 1) % 5 = 4 → "orange"
// Click 5: (4 + 1) % 5 = 0 → "red" ← wraps back automatically
// Click 6: (0 + 1) % 5 = 1 → "blue" ← and keeps going The expression (colorIndex + 1) % colors.length handles the wrap-around in one step — no if statement needed. When the index would exceed the last position, the remainder resets it to zero automatically.
function cycleColor() {
colorIndex = (colorIndex + 1) % colors.length;
text.style.color = colors[colorIndex];
} Both versions behave identically. The if version is easier to read when you are learning; the modulo version is what you will often see in real code. Either is perfectly fine.
A Better Way to Handle Events: addEventListener
So far we have been using onclick="..." directly in HTML. This works, but it mixes HTML and JavaScript together. A cleaner approach is to keep your JavaScript entirely in the <script> tag and attach event listeners from there.
const button = document.querySelector("#my-button");
button.addEventListener("click", function() {
console.log("The button was clicked!");
});addEventListener takes two arguments:
- The event type — a string like
"click" - A function to run when that event fires — called a callback
const button = document.querySelector("#change-btn");
const text = document.querySelector("#mainText");
button.addEventListener("click", function() {
text.style.color = "red";
});onclick attribute from the button in HTML entirely. The JavaScript takes care of the connection. Reading User Input
Buttons are one kind of user interaction. Another is text input — letting the user type something and doing something with what they typed.
HTML provides several input types. The type attribute controls what the input looks like, what the browser accepts, and — most importantly — how you read its value in JavaScript.
<!-- The type attribute controls what the input looks like and what it accepts -->
<input type="text" /> <!-- freeform text -->
<input type="number" /> <!-- numeric keyboard on mobile, blocks letters -->
<input type="checkbox" /> <!-- on/off toggle -->
<input type="radio" /> <!-- pick one from a group -->
<select> <!-- dropdown list -->
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>Text input
The simplest case. The HTML:
<!-- HTML -->
<input type="text" id="nameInput" placeholder="Enter your name" />
<button id="greetBtn">Greet Me</button>
<p id="output"></p> Every <input> element has a .value property. It contains whatever the user has typed into the field.
const input = document.querySelector("#nameInput");
// .value gives you whatever the user typed
console.log(input.value);Putting it together:
const input = document.querySelector("#nameInput");
const button = document.querySelector("#greetBtn");
const output = document.querySelector("#output");
button.addEventListener("click", function() {
const name = input.value;
output.textContent = "Hello, " + name + "!";
});Number input
type="number" gives you a numeric keyboard on mobile and blocks non-numeric characters on desktop. You can also set min and max attributes to restrict the range.
<h1 id="mainText">Hello, World!</h1>
<input type="number" id="sizeInput" value="32" min="10" max="100" />
<button id="applySize">Apply Size</button>const sizeInput = document.querySelector("#sizeInput");
const applySize = document.querySelector("#applySize");
const text = document.querySelector("#mainText");
applySize.addEventListener("click", function() {
const size = sizeInput.value;
text.style.fontSize = size + "px";
});Checkbox
A checkbox represents a true/false toggle. Instead of reading .value, you read .checked — which is true when the box is ticked and false when it is not. The "change" event fires each time the user toggles it.
<label>
<input type="checkbox" id="boldToggle" />
Bold
</label>const boldToggle = document.querySelector("#boldToggle");
const text = document.querySelector("#mainText");
boldToggle.addEventListener("change", function() {
// checkboxes use .checked (true or false), not .value
if (boldToggle.checked) {
text.style.fontWeight = "bold";
} else {
text.style.fontWeight = "normal";
}
}); Notice the eventListener "change." The change event is fired for input, select, and textarea elements when the user modifies the element's value.
Radio buttons
Radio buttons let the user pick exactly one option from a group. The group is defined by giving every radio input the same name attribute — the browser enforces that only one can be selected at a time.
<label><input type="radio" name="textSize" value="small" /> Small</label>
<label><input type="radio" name="textSize" value="medium" /> Medium</label>
<label><input type="radio" name="textSize" value="large" /> Large</label>
<button id="applyRadio">Apply</button> To find out which one is selected, query for the radio in the group that has the :checked pseudo-class:
Select (dropdown)
A <select> element is a dropdown list. Each <option> has a value attribute — that is what you get back from .value when the user makes a selection. Reading it works exactly the same as a text input.
<select id="colorPicker">
<option value="">-- pick a color --</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>const colorPicker = document.querySelector("#colorPicker");
const text = document.querySelector("#mainText");
colorPicker.addEventListener("change", function() {
// select elements use .value just like text inputs
console.log("chosen:", colorPicker.value);
text.style.color = colorPicker.value;
});text, number, select → .value (always a string)checkbox, radio → .checked (boolean) Bonus: Picking a Random Item from an Array
JavaScript has a built-in function called Math.random() that returns a random decimal between 0 and 1 every time you call it.
// Math.random() returns a decimal between 0 and 1 (never exactly 1)
console.log(Math.random()); // e.g. 0.7243
console.log(Math.random()); // e.g. 0.1087
console.log(Math.random()); // e.g. 0.9412 — different every time A decimal on its own is not useful as an array index — we need a whole number. The trick is to scale it up by multiplying by the array length, then chop off the decimal with Math.floor(), which always rounds down to the nearest whole number.
const colors = ["red", "blue", "green", "purple", "orange"];
// Multiply by the array length to get a number between 0 and 5
// Then Math.floor() rounds it down to a whole number: 0, 1, 2, 3, or 4
const index = Math.floor(Math.random() * colors.length);
// if Math.random() gives 0.7243,
// then .7243 * 5 = 3.6215,
// and Math.floor() gives 3
console.log(index); // e.g. 3
console.log(colors[index]); // e.g. "purple" Because Math.random() never quite reaches 1, multiplying by 5 gives a number anywhere from 0 up to (but not including) 5. Math.floor() then turns that into 0, 1, 2, 3, or 4 — exactly the valid indexes for a 5-item array.
Wrapping it in a function:
const colors = ["red", "blue", "green", "purple", "orange"];
function randomColor() {
const index = Math.floor(Math.random() * colors.length);
text.style.color = colors[index];
}Group Work
Continue working on your local text editor file. Try adding these features using what we covered today:
- Replace any
onclickattributes in your HTML withaddEventListenercalls in your script. - Use some of the user input elements we covered today to let the user customize the editor. For example, you could add a color dropdown to change the text color, or a number input to change the font size.or a text input to change the actual text displayed.
- Experiment with using
addEventListener("change")on some of the input elements to see how it behaves compared toaddEventListener("click")on buttons.