Module 2: Programming for the Web

NSWSoftware EngineeringSyllabus dot point

Inquiry Question 2: How can data be better visualised using a web browser?

Use JavaScript in the browser to manipulate the DOM, handle events and make asynchronous requests

A focused answer to the HSC Software Engineering Module 2 dot point on client-side JavaScript. DOM manipulation, event handlers, fetch and async/await, the worked example, and the traps markers look for.

Generated by Claude OpusReviewed by Better Tuition Academy5 min answer

Have a quick question? Jump to the Q&A page

What this dot point is asking

NESA wants you to use JavaScript in the browser to read and modify the DOM, respond to user events, and make asynchronous HTTP requests with fetch. You need to write working code under exam conditions.

The answer

Selecting elements (DOM access)

The DOM (Document Object Model) is a tree representation of the HTML document. JavaScript reads and modifies it through methods like:

const heading = document.querySelector("h1");
const buttons = document.querySelectorAll("button.primary");
const username = document.getElementById("username");

Modifying the DOM

Common operations:

heading.textContent = "Welcome";
heading.classList.add("highlighted");
heading.style.color = "blue";

const newItem = document.createElement("li");
newItem.textContent = "New point";
document.querySelector("ul").appendChild(newItem);

document.querySelector(".old").remove();

Use textContent when inserting user-controlled strings. innerHTML parses and executes HTML, which can introduce XSS.

Events

JavaScript responds to user actions through event listeners:

const button = document.querySelector("#save");

button.addEventListener("click", (event) => {
  console.log("Saved at", event.timeStamp);
});

const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  // ... custom handling, e.g. validation ...
});

Common events: click, submit, input, change, keydown, mouseover, load.

Asynchronous requests

Browser JavaScript exchanges data with the server through the fetch API, which returns a Promise that resolves with the response. Combined with async and await, this lets you write asynchronous code that reads like synchronous code, with try/catch for error handling.

async function loadUsers() {
  const response = await fetch("/api/users", {
    headers: { "Accept": "application/json" },
  });
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  return response.json();
}

// Use it:
loadUsers()
  .then(users => console.log(users))
  .catch(err => console.error(err));

Putting it together

A complete example - a search-as-you-type input:

<input id="search" placeholder="Search...">
<ul id="results"></ul>
const input = document.getElementById("search");
const results = document.getElementById("results");

let timeout = null;
input.addEventListener("input", () => {
  clearTimeout(timeout);
  timeout = setTimeout(async () => {
    const q = encodeURIComponent(input.value);
    const response = await fetch(`/api/search?q=${q}`);
    const items = await response.json();
    results.innerHTML = "";
    for (const item of items) {
      const li = document.createElement("li");
      li.textContent = item.title;
      results.appendChild(li);
    }
  }, 300);
});

The 300 ms debounce prevents a request on every keystroke. encodeURIComponent safely encodes user input for use in a URL.

Variables, types, control flow

JavaScript essentials:

const greeting = "Hello";       // immutable binding
let counter = 0;                // mutable binding
counter += 1;

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2);    // [2, 4, 6, 8]
const even = numbers.filter(n => n % 2 === 0);  // [2, 4]
const sum = numbers.reduce((acc, n) => acc + n, 0);  // 10

function greet(name) {
  if (!name) return "Hello, stranger";
  return `Hello, ${name}`;
}

Security: never inject untrusted HTML

Setting innerHTML with a value that came from user input is one of the most common ways an XSS vulnerability slips into a front-end. The browser parses the assigned string as HTML, so any script tag, event handler attribute, or javascript URL inside it can execute. Use textContent or the DOM API instead.

// BAD - XSS risk
container.innerHTML = `<p>${userInput}</p>`;

// GOOD
const p = document.createElement("p");
p.textContent = userInput;
container.appendChild(p);

Past exam questions, worked

Real questions from past NESA papers on this dot point, with our answer explainer.

2025 HSC5 marksWrite JavaScript that, when a button is clicked, fetches a list of users from /api/users and displays each user's name in an unordered list.
Show worked answer →
<button id="load">Load users</button>
<ul id="users"></ul>
const button = document.getElementById("load");
const list = document.getElementById("users");

button.addEventListener("click", async () => {
  try {
    const response = await fetch("/api/users");
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    const users = await response.json();
    list.innerHTML = "";
    for (const user of users) {
      const item = document.createElement("li");
      item.textContent = user.name;
      list.appendChild(item);
    }
  } catch (err) {
    list.innerHTML = `<li>Error: ${err.message}</li>`;
  }
});

Walkthrough: select the button and the list with getElementById, attach a click handler with addEventListener, fetch the JSON over HTTP, and rebuild the list. await pauses inside the async function until the response and JSON parse complete. Setting textContent (not innerHTML) prevents XSS if a user name happens to contain HTML.

Markers reward a real event listener (not inline onclick), fetch with await, error handling for non-2xx responses, and use of textContent rather than innerHTML for user-controlled data.

Related dot points