Write JavaScript for a web page
Outcome
Section titled “Outcome”You will connect an external JavaScript file to a web page and write a small program that calculates and reports a project’s completion status in the browser console.
Why this matters
Section titled “Why this matters”JavaScript adds behavior to a web page. Before you connect behavior to buttons, forms, and other page elements, you need to know how the browser runs a script and how JavaScript represents data, calculates results, makes decisions, and reuses instructions.
What you will practice
- Load an external JavaScript file with a script element and confirm that it runs in the browser.
- Use string, number, and boolean values with const and let variable declarations.
- Combine values with arithmetic, comparison, and template-literal expressions.
- Use if and else if to select behavior from a boolean condition.
- Define and call a function with parameters and a return value.
- Use console output and error messages to trace and debug a small program.
What is new and what is reused
Section titled “What is new and what is reused”- New: The browser’s JavaScript runtime, external scripts, statements, expressions, primitive values, variables, operators, template literals, conditional branches, functions, parameters, return values, strict mode, and console debugging.
- Reused: HTML document structure, relative file paths, VS Code, browser developer tools, saving files, reloading a page, and reading exact error locations.
Starting point
Before you start
- The completed Level 1 learning goals, including HTML, CSS, local project folders, browser developer tools, and the solo Git workflow.
- VS Code and a current version of Edge, Chrome, or Firefox.
- Permission to create a new folder and two files on your computer.
- No starter files are required.
- Current state
- You do not have a Level 2 JavaScript practice workspace yet.
- First action
- Create a folder named web-interaction-lessons, open it in VS Code, and create index.html and app.js inside it.
- First checkpoint
- The VS Code Explorer shows index.html and app.js at the same level inside web-interaction-lessons.
- Help trigger
- Use the recovery note or ask for help if the browser displays JavaScript as page text, the Console does not show your saved message after a reload, or an error remains after you check the file and line named in the error.
Required result
Section titled “Required result”You have completed the lesson when:
web-interaction-lessonscontainsindex.htmlandapp.js;index.htmlloadsapp.jsthrough an externalscriptelement withdefer;- the Console shows Community event page and 60% complete. Continue with required tasks. in that order;
- the program uses string, number, and boolean values;
- stable project data uses
const, while the status message usesletbecause the program can assign it a different value; - a function receives completed and total task counts and returns a percentage;
- an
ifandelse ifstatement select the blocked or complete status when either condition is true; - changing the test data selects each required status without changing the decision code; and
- the final version runs without a JavaScript error in the Console.
Connect JavaScript to the page
Section titled “Connect JavaScript to the page”JavaScript needs an execution environment. An execution environment supplies the rules and features that make code run. In this lesson, the browser is the execution environment. Later JavaScript work can also run in environments such as Node.js.
Add this document to index.html:
<!doctype html><html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Project status</title> <script src="app.js" defer></script> </head> <body> <main> <h1>Project status</h1> <p>Open the browser Console to read the current status.</p> </main> </body></html>The script element connects the two files:
src="app.js"gives the relative path fromindex.htmlto the JavaScript file beside it.deferlets the browser continue parsing the HTML before it runs the external script.- The element uses both an opening and a closing tag.
scriptis not a void element.
This page puts the script in head and uses defer. Keep this loading method throughout the web-interaction lessons so the project has one predictable script-loading pattern.
Add this code to app.js:
"use strict";
console.log("JavaScript is connected.");"use strict"; is a directive. It enables strict mode for this classic script. Strict mode turns some silent mistakes into errors, which makes those mistakes easier to find.
console.log("JavaScript is connected."); sends a value to the browser Console. It does not add visible content to the web page.
Open the Console
Section titled “Open the Console”- Save
index.htmlandapp.js. - Open
index.htmlin Edge, Chrome, or Firefox. - Open the browser developer tools. You can right-click the page, select Inspect, and then select Console.
- Reload the page while the Console is open.
- Find JavaScript is connected. in the Console.
The Console can contain messages from the browser, an extension, or developer tools. Find your exact message before you treat the connection as confirmed.
If it does not work
Section titled “If it does not work”- If the page shows the JavaScript code as text, confirm that the code is in
app.jsand that the browser openedindex.html. - If no message appears, save both files, reload the page, and confirm that
src="app.js"matches the exact file name. - If the Console shows a red error, open the file-and-line link at the right side of the error. Compare that line with the lesson code.
- If the browser cannot find
app.js, confirm thatindex.htmlandapp.jsare in the same folder.
Checkpoint: The external script runs
- What now works
- The browser renders the Project status page, and the Console shows JavaScript is connected. after each reload.
- Files changed
index.html, app.js- What remains
- Represent the project data, calculate a result, and select a status message.
- Next action
- Return to app.js and replace the first console.log call with the project-data declarations in the next section.
- If it does not work
- Check the saved file names, the script src path, and the first red Console error from top to bottom.
Read statements, expressions, and values
Section titled “Read statements, expressions, and values”JavaScript source code contains several kinds of parts. These terms help you describe where a result or error comes from.
| Term | Example | Purpose |
|---|---|---|
| Value | 3 |
Represents data in the program |
| Expression | completedTasks / totalTasks |
Produces a value |
| Statement | const completedTasks = 3; |
Performs an action, such as declaring a variable |
| Code block | { statusMessage = "Ready for review."; } |
Groups statements for a function or condition |
| Comment | // Stable project data |
Leaves information for a person reading the source |
A semicolon ends each statement in this lesson. JavaScript can insert some semicolons automatically, but explicit semicolons keep the boundary visible while you learn the syntax.
Replace the first console.log call in app.js with these declarations:
"use strict";
// Stable project dataconst projectName = "Community event page";const completedTasks = 3;const totalTasks = 5;const hasBlockingIssue = false;
console.log(projectName);console.log(completedTasks);console.log(totalTasks);console.log(hasBlockingIssue);The program now uses three primitive value types:
| Type | Lesson examples | Use |
|---|---|---|
| String | "Community event page" |
Text |
| Number | 3 and 5 |
Numeric data and calculations |
| Boolean | false |
One of two logical values: true or false |
JavaScript has more value types. These three types are enough for the required program. Later lessons introduce structured values when the interface needs to manage several related records.
Bind each value to a name
Section titled “Bind each value to a name”A variable binds a value to an identifier. An identifier is the name that the code uses to refer to that value, such as projectName.
Use const when the binding will not receive a different value after its declaration. All four project-data bindings remain stable during one run, so they use const.
Use let when the program must assign a different value to the same binding. The status message will use let later because a condition can replace its initial value.
Use names that describe the data. JavaScript identifiers are case-sensitive, so projectName and projectname are different identifiers.
Inspect the value types
Section titled “Inspect the value types”Add these temporary lines at the end of app.js:
console.log(typeof projectName);console.log(typeof completedTasks);console.log(typeof hasBlockingIssue);Save the file and reload the page. The last three Console results are string, number, and boolean.
The typeof operator produces a string that identifies the value’s type. Remove the three temporary lines after you confirm the results. Keep the four project-data console.log calls for the next check.
The Console must show these values in this order:
Community event page35falseChange completedTasks from 3 to 4, save, reload, and confirm that the second Console value changes. Restore it to 3 before you continue.
Calculate a completion percentage
Section titled “Calculate a completion percentage”An operator combines or examines values. The calculation uses arithmetic operators:
(completedTasks / totalTasks) * 100The division operator / calculates the completed part of the total. The multiplication operator * converts that part to a percentage. Parentheses make the intended grouping explicit.
Put the calculation inside a function. Add this function after the four project-data declarations and before the console.log calls:
function calculateCompletion(completed, total) { if (total === 0) { return 0; }
return Math.round((completed / total) * 100);}A function groups instructions under one name. The function does not calculate a result until the program calls it.
Read the function from the outside in:
calculateCompletionis the function name.completedandtotalare parameters. They are local identifiers for the input values that one call supplies.total === 0is a comparison expression. The strict equality operator===producestruewhen the two values have the same type and value.- The first
returnhandles a zero total before division. This guard prevents the program from reporting an unusable division result. Math.round(...)calls a method on JavaScript’s built-inMathobject and returns the calculation rounded to the nearest integer.- The final
returnsends that integer back to the code that called the function.
Add this function call after the function declaration:
const completionPercentage = calculateCompletion( completedTasks, totalTasks,);The values completedTasks and totalTasks are arguments. The call supplies them to the function’s completed and total parameters. The returned number becomes the value of completionPercentage.
Replace the four console.log calls with these two calls:
console.log(projectName);console.log(completionPercentage);Save app.js and reload the page. The Console now shows:
Community event page60Trace the calculation
Section titled “Trace the calculation”For the current data, the program evaluates the expression in this order:
- The call assigns
3tocompletedand5tototalfor this function run. total === 0producesfalse, so the firstreturndoes not run.3 / 5produces0.6.0.6 * 100produces60.Math.round(60)produces60.returnsends60back to the call.completionPercentagereceives60.
Checkpoint: The function returns a calculated result
- What now works
- The Console shows Community event page followed by 60, and changing completedTasks changes the calculated percentage after a reload.
- Files changed
app.js- What remains
- Convert the number into a status message and select special messages for blocked and complete work.
- Next action
- Keep completedTasks at 3 and add the status-message code after the completionPercentage declaration.
- If it does not work
- Log completedTasks, totalTasks, and completionPercentage separately. Check the argument order and confirm that calculateCompletion returns the Math.round result.
Select a status with conditions
Section titled “Select a status with conditions”A conditional statement runs a code block only when its condition produces true.
Add this code after the completionPercentage declaration and before the final console.log calls:
let statusMessage = `${completionPercentage}% complete. Continue with required tasks.`;
if (hasBlockingIssue) { statusMessage = "Blocked: resolve the current issue.";} else if (completionPercentage === 100) { statusMessage = "Ready for review.";}Replace the second console.log argument so the final output uses statusMessage:
console.log(projectName);console.log(statusMessage);The first status uses a template literal. Backticks mark the complete string. ${completionPercentage} evaluates the expression and inserts its value into the text.
The code declares statusMessage with let because a true condition assigns a different string to that binding.
The branches run in order:
if (hasBlockingIssue)checks the boolean value directly.- When that value is
true, the program assigns the blocked message and skips theelse ifbranch. - When the first condition is
false,else ifcompares the percentage with100. - When both conditions are
false, neither block runs, so the initial progress message remains.
Test every branch
Section titled “Test every branch”Change only the project data at the top of app.js. Save and reload after each test.
| Test data | Expected second Console message | Selected path |
|---|---|---|
completedTasks = 3, totalTasks = 5, hasBlockingIssue = false |
60% complete. Continue with required tasks. |
Initial message remains |
completedTasks = 5, totalTasks = 5, hasBlockingIssue = false |
Ready for review. |
else if block |
completedTasks = 3, totalTasks = 5, hasBlockingIssue = true |
Blocked: resolve the current issue. |
First if block |
The blocked test also shows that branch order is a product decision. A project with all tasks complete but a blocking issue still reports Blocked because the program checks that condition first.
Restore the first row’s data before the final verification.
If it does not work
Section titled “If it does not work”- If the Console shows
ReferenceError, check the identifier spelling and letter case. Also confirm that the declaration appears before the code that uses it. - If the Console shows
SyntaxError, check the named line and the line before it for a missing quote, backtick, parenthesis, brace, or semicolon. - If the message contains
NaN, log both function arguments. Confirm that both arguments are numbers and that both declarations exist. - If every test shows the same status, log
hasBlockingIssueandcompletionPercentagebefore the conditional statement. Confirm that you saved and reloaded after each data change. - If
60appears instead of the full message, confirm that the secondconsole.logusesstatusMessage.
Review the complete program
Section titled “Review the complete program”Your final app.js must match this structure:
"use strict";
// Stable project dataconst projectName = "Community event page";const completedTasks = 3;const totalTasks = 5;const hasBlockingIssue = false;
function calculateCompletion(completed, total) { if (total === 0) { return 0; }
return Math.round((completed / total) * 100);}
const completionPercentage = calculateCompletion( completedTasks, totalTasks,);
let statusMessage = `${completionPercentage}% complete. Continue with required tasks.`;
if (hasBlockingIssue) { statusMessage = "Blocked: resolve the current issue.";} else if (completionPercentage === 100) { statusMessage = "Ready for review.";}
console.log(projectName);console.log(statusMessage);The code has one data flow:
project data -> calculateCompletion arguments -> returned percentage -> conditional checks -> selected statusMessage -> Console outputThe arrows show which result becomes the next input. They are an explanation of the program, not JavaScript syntax.
Self-check
Complete these checks against the required result.
- Confirm that index.html and app.js are saved in the same folder and that the script element uses src="app.js" and defer.
- Reload the page and confirm that the Console shows Community event page and 60% complete. Continue with required tasks. in that order.
- Point to one string, number, and boolean value in app.js and state what each value represents.
- Explain why the project-data bindings use const and statusMessage uses let.
- Trace the values 3 and 5 from the function arguments through the parameters to the returned value 60.
- Set completedTasks to 5 and confirm that the complete branch reports Ready for review.
- Set hasBlockingIssue to true and confirm that the first branch reports the blocked message.
- Restore completedTasks to 3 and hasBlockingIssue to false, then confirm that no red JavaScript error remains in the Console.
Checkpoint: The JavaScript result is complete
- What now works
- The external script calculates a percentage, selects the correct status for all three test cases, and reports the restored 60% result without a Console error.
- Files changed
index.html, app.js- What remains
- Connect JavaScript behavior to elements and user events in the next lesson.
- Next action
- Keep this workspace and its restored project data. Open the DOM-events lesson when you are ready to make the page respond to user input.
- If it does not work
- Compare app.js with the complete program, then fix the first Console error before you test later behavior.
Reference pattern: choose a random result within known boundaries
Section titled “Reference pattern: choose a random result within known boundaries”Some later projects need an unpredictable result. Use this section when a project asks for a percentage chance or one random item from an array. It is a reference pattern, not another requirement for the project-status program above.
JavaScript’s Math.random() method returns one number that is:
- greater than or equal to
0; and - less than
1.
The result can be 0, but it cannot be 1. Read the official references for Math.random() and Math.floor() when you need the complete API details.
Make one percentage decision
Section titled “Make one percentage decision”This neutral example gives a review task a 25% chance of being selected:
const selectionPercentage = 25;const selectionThreshold = selectionPercentage / 100;const chanceValue = Math.random();const shouldSelectReview = chanceValue < selectionThreshold;25 / 100 produces the threshold 0.25. The comparison produces one boolean value:
chanceValue |
Comparison | shouldSelectReview |
|---|---|---|
0 |
0 < 0.25 |
true |
0.249 |
0.249 < 0.25 |
true |
0.25 |
0.25 < 0.25 |
false |
0.999 |
0.999 < 0.25 |
false |
The boundary belongs to the product rule. If the rule says 25%, keep the same percentage and comparison until you deliberately change that rule.
Select one valid array item
Section titled “Select one valid array item”Use a separate random value when a successful chance decision must select one item:
const reviewAreas = ["keyboard", "layout", "console"];const selectionValue = Math.random();const reviewIndex = Math.floor(selectionValue * reviewAreas.length);const selectedReviewArea = reviewAreas[reviewIndex];The array contains three items, so its valid indexes are 0, 1, and 2. Multiplication changes the range from 0–less-than-1 to 0–less-than-3. Math.floor() rounds each value down to the nearest integer:
selectionValue |
Value before Math.floor() |
reviewIndex |
|---|---|---|
0 |
0 |
0 |
0.499 |
1.497 |
1 |
0.999 |
2.997 |
2 |
Because Math.random() cannot return 1, this calculation cannot produce index 3. If the array length changes, the same calculation uses the new length and still selects a valid index.
Keep the two random responsibilities separate
Section titled “Keep the two random responsibilities separate”Use this order when one action needs both decisions:
- Create one random value for the percentage-chance decision.
- Stop if the chance decision fails.
- Create a second random value to select one valid array index.
- Store the selected item in the program’s state or another stable variable.
- Render from that stored result.
Do not generate either random value inside a render function. A render can run again after resizing, focus restoration, or another state change. The selected result must remain stable until a user action or product rule deliberately replaces it.
Check the boundaries
Section titled “Check the boundaries”Temporarily replace chanceValue with 0.249 and then 0.25. Confirm that the two values select different sides of the 25% boundary. Restore Math.random() afterward.
Temporarily replace selectionValue with 0, 0.499, and 0.999. Confirm that the example selects indexes 0, 1, and 2. Restore Math.random() and confirm that no Console error remains.
Common JavaScript error types
Section titled “Common JavaScript error types”Console errors name the type of failure and usually identify a file and line. Start with the first error because one early syntax error can prevent later statements from running.
| Error type | Typical meaning in this lesson | First check |
|---|---|---|
SyntaxError |
The browser cannot parse part of the source | Check punctuation on the named line and the line before it |
ReferenceError |
The code uses an identifier that is not available | Check spelling, letter case, and declaration order |
TypeError |
An operation does not work with the current value | Check the value and the operation named in the message |
Do not delete working sections at random. Read the first error, inspect its location, correct one cause, save, and reload. This keeps the test connected to one hypothesis.
Next step or safe stopping point
Section titled “Next step or safe stopping point”The required lesson is complete when the browser runs the external script, the final Console result is restored, all three conditional paths pass their tests, and you can trace the data from declarations to output.
Continue to Respond to user input with DOM events to select page elements and run JavaScript when a user activates a control.
If you stop here, leave yourself this resume note: The project-status calculation works in the Console. Next, open the DOM-events lesson and connect the working JavaScript to page elements and a user action.