Web Development (COMP COMP1021)
<h1>Welcome to COMP1021!</h1>
About Your Professor
👋 Hi, I'm Graham! I'm a St. Lawrence College graduate, and I work in the industry as an Software Engineer. I have two cats and two dogs, and I really enjoy teaching!
I got drawn to programming as the logical thinking and problem-solving required really works well for my brain, and I find it incredibly satisfying to finally crack a sneaky bug.
About This Playbook
Web Programming (COMP1021) Playbook - "The Playbook", is a tool provided to you in order to assist with the course material. It is structured in approximately the same way the course is, and will be a valuable resource throughout the course.
The Playbook is a supplement only!
This Playbook is an evolving book, and it may update periodically with clarifications, so the content may change slightly. The ultimate source of truth for the course material is Blackboard, and this Playbook is provided as a supplement only. Please refer to Blackboard directly for important handouts such as the Course Outline, Learning Plan, and due dates.
Reference
This Playbook can be considered a reference guide for the course. We will refer to this Playbook regularly for informational content and explanations of the course material, including both during lectures and labs.
Exercises
Throughout this book, there will be exercises noted which will help solidify the content within the course. You are highly encouraged to complete these exercises, and they will often be part of the lecture with time provided to discuss them.
Code Blocks
This book will share code snippets in Code Blocks, which you've already seen at the top of this page. This allows the code to be read easily as it will have proper syntax highlighting, but also allows you to copy code snippets directly out of this book. To copy the code snippets, hover over the snippet and there is a Copy button at the top right.
<!-- Hover over this code block to see a copy button! -->
<h1>Welcome to COMP1021!</h1>
<p>This code is contained in a code block!</p>
Extra Resources
In this course we will use a combination of resources provided directly to you, as well as external resources such as MDN Web Docs. Please refer to the Extra Resources section of the playbook for an up-to-date list of resources.
COMP 205: Web Development
COMP 205 (Web Development) and COMP 1021 (Web Programming) are co-requisites. The two courses are designed to complement each other, and the material in each supports the other.
COMP 205 focuses on HTML, CSS, the visual structure of the web page, and front-end development. COMP 1021 focuses on server-side programming, and building dynamic web applications.
The COMP 205 Playbook is available at comp205.grahamcorcoran.ca. You may be referred to it throughout this course when topics overlap, particularly around HTML and CSS.
Module 1: Client & Server-side Scripting
This module covers JavaScript and the two main environments where it runs: the server (using Node.js) and the browser.
We start with the JavaScript language itself, covering variables, data types, operators, and functions. From there, we move into Node.js, JavaScript runtimes, and then into HTML, the DOM, and how JavaScript interacts with web pages in the browser.
Client-side and Server-side
In web development, client-side refers to code that runs in the user's browser. When someone visits a web page, the browser downloads the HTML, CSS, and JavaScript, and runs it locally on their machine. This is the client.
Server-side refers to code that runs on a remote machine (the server) before anything is sent to the browser. The server handles things like processing data, connecting to databases, and deciding what content to send back to the client.
JavaScript is one of the few languages used on both sides. In this module, we cover both.
By the end of this module, you should be comfortable writing JavaScript, running it in both Node.js and the browser, and understanding the distinction between the two environments.
Introduction to JavaScript
JavaScript is the programming language we will use throughout this course. If you are coming from a language like C++, many of the core concepts will feel familiar. Variables, operators, and data types all exist in JavaScript, but the syntax and behaviour differ in some important ways.
JavaScript does not require you to declare types when creating variables. It uses dynamic typing, meaning the type of a variable is determined by the value it holds, and can change during execution.
Variables
Variables in JavaScript are declared using one of three keywords: let, const, or var.
let
let is the standard way to declare a variable in JavaScript. It creates a variable that can be reassigned.
let score = 0;
score = 10;
Variables declared with let are block-scoped, meaning they only exist within the block (such as a function or an if statement) where they are declared.
const
const declares a variable that cannot be reassigned after it is set. Use const when you have a value that should not change.
const pi = 3.14159;
pi = 3; // This will cause an error
If a value should stay the same throughout your program, prefer const over let. It makes your code easier to read because anyone looking at it knows that value will not change.
const prevents reassignment of the variable, but it does not make the value itself immutable. If the value is an array or an object, you can still modify its contents.
const colours = ["red", "blue"];
colours.push("green"); // This works
colours = ["yellow"]; // This causes an error
The variable colours still points to the same array. You are changing what is inside the array, not replacing the array itself. This is why you will often see const used with arrays and objects in real-world code.
var
var is the original way to declare variables in JavaScript. You will see it in older code and in many online examples.
var name = "Fred";
In this course, we use let and const instead of var. The main reason is scoping: var is function-scoped rather than block-scoped, which can lead to unexpected behaviour. You do not need to understand the details of this difference right now, but if you see var in examples online, know that let is the modern replacement.
Data Types
JavaScript has several built-in data types. The most common ones you will work with are:
| Type | Example | Description |
|---|---|---|
| String | "Hello" | Text, wrapped in quotes |
| Number | 42, 3.14 | Integers and decimals (no separate types) |
| Boolean | true, false | Logical values |
| Null | null | An intentionally empty value |
| Undefined | undefined | A variable that has been declared but not assigned a value |
Unlike C++, JavaScript does not distinguish between integers and floating-point numbers. Both 42 and 3.14 are just Number.
You can check the type of a value using typeof:
let age = 25;
console.log(typeof age); // "number"
let name = "Jane";
console.log(typeof name); // "string"
Operators
Most operators in JavaScript work the same way as in C++. Arithmetic operators (+, -, *, /, %) and assignment operators (=, +=, -=) behave as you would expect.
The key difference is with comparison operators. JavaScript has two types of equality check:
| Operator | Name | Description |
|---|---|---|
== | Loose equality | Compares values after converting types |
=== | Strict equality | Compares values and types without conversion |
let a = 5;
let b = "5";
console.log(a == b); // true (string "5" is converted to number 5)
console.log(a === b); // false (number and string are different types)
Use === (strict equality) by default. Loose equality can produce unexpected results because of the type conversion. The same applies to !== (strict not equal) over != (loose not equal).
In C++, every variable has a type declared at compile time. If you compare an int to a string, the compiler will either reject it or you have to explicitly convert the type yourself. JavaScript is dynamically typed, so a variable can hold any type of value at any time. This means two values of completely different types can end up being compared, and JavaScript will try to convert them automatically with loose equality (==). Strict equality (===) avoids this by checking the type first and returning false immediately if the types do not match.
Console Output
console.log() is the primary way to output information in JavaScript. It prints to the terminal when running with Node, or to the browser's developer console when running in a browser.
console.log("Hello, world!");
You can output multiple values by separating them with commas:
let course = "COMP1021";
let year = 2026;
console.log("Welcome to", course, year);
// Welcome to COMP1021 2026
You can also join strings together using the + operator:
let name = "Graham";
console.log("Hello, " + name + "!");
// Hello, Graham!
The preferred way to include variables in strings is with template literals. Template literals use backticks (`) instead of quotes, and variables are inserted using ${}:
let name = "Graham";
let course = "COMP1021";
console.log(`Hello, ${name}! Welcome to ${course}.`);
// Hello, Graham! Welcome to COMP1021.
Template literals are easier to read than string concatenation, especially when you have multiple variables. Basic concatenation with + still works and you will see it in plenty of code, but template literals are the modern standard.
Comments
Comments in JavaScript work similarly to C++.
Single-line comments use //:
// This is a comment
let x = 10; // This is also a comment
Multi-line comments use /* */:
/*
This comment spans
multiple lines.
*/
Comments are ignored when the code runs. Use them to explain parts of your code that are not obvious from reading the code itself.
Functions in JavaScript
Functions are a way to group reusable code together. Instead of writing the same logic multiple times, you write it once inside a function and call it whenever you need it.
If you have written functions in C++, the concept is the same. The syntax is different, but the idea of defining a block of code that accepts input and produces output carries over directly.
Declaring Functions
A function in JavaScript is declared using the function keyword, followed by a name, parentheses, and a block of code inside curly braces.
function greet() {
console.log("Hello!");
}
To run the code inside the function, you call it by name with parentheses:
greet(); // prints "Hello!"
A function will not run until it is called. The declaration on its own does nothing.
Here is a more complete example with the components labeled:
// "add" is the function name
function add(num1, num2) { // num1 and num2 are parameters
// everything in here is the function body
return num1 + num2; // the return statement sends a value back
}
let result = add(3, 4); // 3 and 4 are arguments
console.log(result); // 7
Parameters and Arguments
Parameters are the variables listed in a function's declaration. Arguments are the actual values you pass in when calling the function.
function multiply(a, b) { // a and b are parameters
return a * b;
}
multiply(5, 3); // 5 and 3 are arguments
You can have as many parameters as you need, or none at all. If a function is called with fewer arguments than it has parameters, the missing ones will be undefined.
function introduce(name, age) {
console.log(`My name is ${name} and I am ${age} years old.`);
}
introduce("Priya"); // My name is Priya and I am undefined years old.
It's worth highlighting the above code snippet. Unlike most other languages, JavaScript will not stop you from calling a function with the wrong number of arguments. Missing parameters silently become undefined, which can lead to unexpected results like NaN (Not a Number) when used in calculations. If something isn't working and you can't figure out why, check that you're passing all the expected arguments.
Return Values
The return keyword sends a value back from a function to wherever it was called. You can store this value in a variable or use it directly.
function square(n) {
return n * n;
}
let result = square(6);
console.log(result); // 36
console.log(square(4)); // 16
A function can only return once. When a return statement is reached, the function stops executing immediately. Any code after the return will not run.
function check(value) {
if (value > 10) {
return "big";
}
return "small";
console.log("this will never run");
}
If a function does not have a return statement, it returns undefined by default.
Scope
Scope determines where a variable is accessible in your code. In the Introduction to JavaScript chapter, we mentioned that let and const are block-scoped. Functions create their own scope.
Variables declared inside a function are only accessible within that function:
function calculateTotal(price, tax) {
let total = price + (price * tax);
return total;
}
calculateTotal(50, 0.13);
console.log(total); // Error: total is not defined
The variable total exists inside calculateTotal and cannot be accessed outside of it.
Variables declared outside of a function are accessible inside it:
let taxRate = 0.13;
function calculateTotal(price) {
return price + (price * taxRate);
}
console.log(calculateTotal(50)); // 56.5
This works, but relying on variables from outside a function can make your code harder to follow. Where possible, pass values in as parameters so the function is self-contained.
Introduction to Node.js
Node.js is a tool that lets you run JavaScript outside of a web browser. Instead of needing an HTML page to execute your code, you can run JavaScript directly from your terminal.
Installing Node.js
To check if Node.js is installed, open a terminal and run:
node --version
If you see a version number, you're good to go. If not, follow the official installation guide at https://nodejs.org/. The LTS (Long Term Support) version is recommended.
Installing Node.js also installs npm, which is covered below.
Running JavaScript with Node
To run a JavaScript file with Node, use the node command followed by the file name:
node myFile.js
Node reads the file, executes the JavaScript inside it, and outputs any results to the terminal. If your code includes console.log(), the output will appear in the terminal where you ran the command.
let greeting = "Hello from Node";
console.log(greeting);
node myFile.js
Hello from Node
Unlike running JavaScript in a browser, there is no web page involved. Node executes the code and exits. This makes it useful for writing scripts, building tools, and running server-side applications.
npm
npm (Node Package Manager) is a command-line tool that comes bundled with Node.js. It is used to manage packages, which are third-party libraries written by other developers that you can use in your own projects.
Most real-world projects rely on external packages rather than building everything from scratch. For example, if your application needs to connect to a database or run a web server, there are well-tested packages available that handle this for you. npm is how you find, install, and keep track of those packages.
Initializing a Project
To start using npm in a project, you first initialize it:
npm init
This walks you through a series of prompts and creates a package.json file in your project folder. If you want to skip the prompts and accept the defaults, you can use:
npm init -y
The package.json file keeps track of your project's metadata and its dependencies. When your project uses external packages, they are recorded here so that anyone else working on the project knows what is required.
Installing Packages
To install a package, use npm install followed by the package name:
npm install express
This does two things:
- Downloads the package into a folder called
node_modulesin your project directory. - Adds the package to the
dependencieslist in yourpackage.json.
You can then use the package in your code with require(), which loads the package so you can use it in your file:
const express = require("express");
The require() function looks for the package by name inside your node_modules folder and returns it. You store the result in a variable (in this case express) and use that variable to access the package's functionality.
The node_modules folder can get large. You should not include it when submitting work or committing to git. Since your dependencies are listed in package.json, anyone can recreate the node_modules folder by running:
npm install
This reads package.json and downloads everything listed in it.
JavaScript and Runtimes
In this course we will be using JavaScript heavily, and while doing so we need to understand the difference between JavaScript the programming language, a JavaScript runtime, and the tools we use surrounding these concepts.
JavaScript
JavaScript is a programming language that was created to further expand the functionality of HTML and CSS in a web context. It was originally built to run inside web browsers, allowing developers to make web pages interactive.
JavaScript is a scripting language, meaning it is interpreted at the time it runs rather than compiled ahead of time like C++. You write the code, and a runtime reads and executes it directly.
An important distinction to keep in mind is that JavaScript is just the language. Where and how it runs depends on the runtime.
Runtimes
A JavaScript runtime is the process that executes the JavaScript code, and handles the result. Since JavaScript is interpreted, it always needs a runtime to execute. The runtime is responsible for reading your code, understanding it, and producing the output.
There are two main runtimes we will use in this course: Node.js and web browsers. Both run JavaScript, but they provide different capabilities depending on the environment they operate in.
Node
Node.js is a JavaScript runtime that runs outside of the browser. When you run node lab3.js in your terminal, Node is the program reading and executing your JavaScript code.
Under the hood, Node uses Google's V8 engine, which is the same JavaScript engine used in Google Chrome. V8 handles the actual interpretation and execution of your code. Node wraps V8 and adds functionality that would not be available in a browser, such as reading and writing files, accessing the operating system, and running a web server.
This is a key point: JavaScript in a browser is sandboxed. It can only interact with the web page it is running on. Node does not have this restriction. It runs on your machine with access to your file system, network, and other system resources. This is what makes it suitable for server-side development.
Node also comes with npm (Node Package Manager), which is a tool for installing and managing third-party libraries. When you need functionality that is not built into JavaScript or Node itself, npm allows you to pull in packages written by other developers. We will use npm regularly throughout this course.
Node takes JavaScript out of the browser and gives it the tools needed to build backend applications, scripts, and tooling.
Web Browsers
Web browsers (Chrome, Firefox, Edge, Safari) are the other major JavaScript runtime. When a browser loads a web page that contains JavaScript, the browser's built-in engine executes that code. Chrome uses V8 (the same engine as Node), Firefox uses SpiderMonkey, and Safari uses JavaScriptCore.
Where Node provides access to the file system and operating system, browsers provide access to the web page itself. JavaScript running in a browser can read and modify the page content, respond to user interactions like clicks and key presses, and make network requests to load additional data.
As mentioned earlier, browser JavaScript is sandboxed. It cannot access files on the user's computer or interact with the operating system directly. This is intentional. When you visit a website, the JavaScript on that page should not be able to read your documents or install software. The browser enforces these restrictions for security.
HTML DOM
The HTML DOM and HTML itself are regularly confused with each other but are not exactly the same. HTML is the markup you write in your .html files. The DOM is the live, in-memory structure the browser creates from that HTML. The browser can also modify the DOM after the page loads (and so can JavaScript), meaning the DOM may not always match the original HTML source.
The DOM (Document Object Model) is the browser's representation of an HTML page. When a browser loads an HTML file, it parses the HTML and builds a structured model of the page in memory. This model is the DOM.
JavaScript in the browser interacts with the page through the DOM. Every HTML element on the page becomes an object in the DOM that JavaScript can access, modify, or remove. For example, JavaScript can change the text inside a <p> tag, add a new <li> to a list, or hide an element when a button is clicked.
The DOM is not part of the JavaScript language. It is an API provided by the browser. This is why DOM-related code like document.getElementById() works in a browser but not in Node. Node does not have a web page, so it has no DOM.
The DOM & JavaScript in the Browser
Up to this point, we have been running JavaScript with Node in the terminal. In this chapter, we shift to running JavaScript in the browser, where it can interact with web pages directly.
HTML Recap
HTML (HyperText Markup Language) is the language used to structure web pages. A basic HTML page looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Page</title>
</head>
<body>
<h1>Hello</h1>
<p>This is a paragraph.</p>
</body>
</html>
HTML is covered in depth in COMP 205, which is the co-requisite for this course. If you need a refresher on HTML elements, attributes, or page structure, refer to the COMP 205 Playbook.
For this chapter, the key thing to understand is that HTML defines the structure of a page using elements, and those elements can be nested inside each other.
The DOM
When a browser loads an HTML file, it does not work with the raw text directly. Instead, it parses the HTML and builds a tree structure in memory called the DOM (Document Object Model).
Each HTML element becomes a node in this tree. Elements nested inside other elements become child nodes. For example, given this HTML:
<body>
<h1>Welcome</h1>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</body>
The browser builds a tree that looks roughly like this:
body
├── h1
│ └── "Welcome"
├── ul
│ ├── li
│ │ └── "Item 1"
│ └── li
│ └── "Item 2"
JavaScript in the browser interacts with this tree. When you change an element's text, add a new element, or remove one, you are modifying the DOM. The browser then updates the page to reflect those changes.
Adding JavaScript to a Page
To run JavaScript in a browser, you include it in your HTML file using the <script> tag.
Inline Scripts
It is possible to write JavaScript directly inside a <script> tag in your HTML:
<body>
<h1>Hello</h1>
<script>
console.log("This runs in the browser");
</script>
</body>
You may see this in online tutorials and examples. In this course, we do not use inline scripts. JavaScript should be kept in separate .js files. Inline scripts mix your logic into your HTML, which makes both harder to read and maintain.
External Scripts
Instead of inline scripts, the standard approach is to keep your JavaScript in a separate file and reference it:
<body>
<h1>Hello</h1>
<script src="app.js"></script>
</body>
// app.js
console.log("This runs in the browser");
Script Placement
Place your <script> tag at the bottom of the <body>, just before the closing </body> tag. This ensures the HTML elements on the page have been loaded before your JavaScript tries to access them. If your script runs before the elements exist, it will not be able to find them.
<body>
<h1>Hello</h1>
<p id="greeting">Welcome to the site.</p>
<!-- Scripts go here, after the content -->
<script src="app.js"></script>
</body>
You may see scripts placed in the <head> of a page with a defer attribute:
<head>
<script src="app.js" defer></script>
</head>
The defer attribute tells the browser to download the script immediately but wait to execute it until the HTML has finished loading. This achieves the same result as placing the script at the bottom of the body. Either approach is acceptable in this course.
Selecting Elements
Before you can do anything with an element on the page, you need to select it. JavaScript provides several methods for this through the document object, which represents the entire DOM. The most common is getElementById, which selects a single element by its id attribute:
<p id="message">Hello there.</p>
let element = document.getElementById("message");
console.log(element.textContent); // "Hello there."
Each id should be unique on the page, so this always returns one element. Other selection methods like querySelector and querySelectorAll exist for more flexible lookups using CSS selectors, but getElementById is sufficient for most of what we will do in this course.
Modifying Elements
Once you have selected an element, you can change its content. The textContent property gets or sets the text inside an element:
<p id="status">Waiting...</p>
let status = document.getElementById("status");
status.textContent = "Ready!";
The page will now display "Ready!" instead of "Waiting...". JavaScript can also modify an element's styles, attributes, and inner HTML, but for now textContent is the main one to be aware of.
Event Handling
Events are things that happen on the page: a user clicks a button, types in a field, hovers over an element, or submits a form. JavaScript lets you listen for these events and run code in response.
addEventListener
The standard way to handle events is with addEventListener. You call it on an element, pass in the event type and a function to run when the event occurs:
<button id="myButton">Click me</button>
let button = document.getElementById("myButton");
button.addEventListener("click", function() {
console.log("Button was clicked!");
});
When the button is clicked, the function runs. This function is called an event handler.
Common Events
| Event | Fires when... |
|---|---|
click | The element is clicked |
input | The value of an input field changes |
submit | A form is submitted |
keydown | A key is pressed |
mouseover | The cursor moves over the element |
Using Event Data
Event handlers receive an event object with information about what happened. You access it by adding a parameter to your handler function:
<input type="text" id="nameField" placeholder="Enter your name">
<p id="preview"></p>
let input = document.getElementById("nameField");
let preview = document.getElementById("preview");
input.addEventListener("input", function(event) {
preview.textContent = `You typed: ${event.target.value}`;
});
The event.target refers to the element that triggered the event. In this case, event.target.value gives you the current contents of the input field.
Putting It Together
Here is a complete example that combines selecting, modifying, and event handling:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Counter</title>
</head>
<body>
<h1>Counter</h1>
<p id="count">0</p>
<button id="increment">Add 1</button>
<script src="counter.js"></script>
</body>
</html>
// counter.js
let count = 0;
let display = document.getElementById("count");
let button = document.getElementById("increment");
button.addEventListener("click", function() {
count = count + 1;
display.textContent = count;
});
This page displays a number and a button. Each time the button is clicked, the number increases by one. The JavaScript selects the elements, listens for a click, updates the variable, and modifies the DOM to reflect the new value.
Test 1 Study Guide
The first test of the course covers Module 1: Client & Server-side Scripting. You can review the material on the following pages:
- Client & Server-side Scripting (Overview)
- Introduction to JavaScript
- Functions in JavaScript
- Introduction to Node.js
- JavaScript and Runtimes
- The DOM & JavaScript in the Browser
Key Concepts
In addition to reviewing the content above, you should feel comfortable answering the following questions:
- What is the difference between client-side and server-side code?
- What is a JavaScript runtime, and why does JavaScript need one?
- Name the two main JavaScript runtimes used in this course, and describe how they differ.
- What is the difference between
let,const, andvar? Which do we use in this course, and why? - Can the contents of an array declared with
constbe modified? Why or why not? - What is the difference between
==and===in JavaScript? Which should you use by default? - What happens if you call a JavaScript function with fewer arguments than it has parameters?
- What is the difference between a parameter and an argument?
- What does
npmstand for, and what is it used for? - What is the DOM, and how does it differ from the HTML source file?
- Why does
document.getElementById()work in a browser but not in Node.js? - Why should
<script>tags be placed at the bottom of the<body>? - Describe what happens when a user clicks a button that has an event listener attached to it.