Client-Side Web Applications

HTML & the DOM


Learning Objectives

  • You can distinguish HTML source, the DOM, and visible rendering.
  • You can inspect and make a small event-driven document change.
  • You can explain why local document changes do not establish server-side state.

The HTML Quick Reference explains how to write markup. This chapter asks a different question: what does the browser construct from that markup, and what do our programs change? Understanding this boundary helps explain both plain JavaScript and component frameworks.

Source is not the current document

HTML source is text. The browser parses that text into a document containing objects connected in a tree: the Document Object Model, or DOM. Elements are nodes in this tree; text is also represented by nodes. Whitespace can create text nodes, so counting child nodes is not the same as counting child elements.

Consider a room-status display:

<main>
<h1>Study room</h1>
<p id="room-status">Available</p>
<button type="button" id="reserve-room">Reserve room</button>
</main>

A simplified element tree is shown in Figure 1. Each arrow connects an element to one of its child elements; text nodes are omitted from this simplified view.

Course diagram
Figure 1. The room-status markup forms a tree of child elements beneath main.

The browser also computes style and layout before painting the page. The DOM describes document structure; it is not itself the pixels on the screen. Read the DOM introduction for the platform model behind these objects.

A small standalone experiment

Save the following as dom-lab.html at the root of the practice application (practice/), outside client/src. Open it directly in a browser. This is an independent HTML experiment, not a replacement for SvelteKit’s src/app.html.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Room status</title>
</head>
<body>
<main>
<h1>Study room</h1>
<p id="room-status" aria-live="polite">Available</p>
<button type="button" id="reserve-room">Reserve room</button>
</main>
<script>
const status = document.querySelector('#room-status');
const button = document.querySelector('#reserve-room');
button.addEventListener('click', () => {
status.textContent = 'Reserved locally';
button.disabled = true;
});
</script>
</body>
</html>

The script runs after these elements have been parsed. The button is a native button, not a clickable paragraph that would require us to rebuild keyboard behavior. aria-live identifies text whose later changes may need to be announced; Part 6 studies this more systematically.

Open the developer tools’ Elements view. Click the button and inspect the paragraph again. Its current text and the button’s disabled state changed. The original file on disk did not change. Reloading restores the original state because this example has no persistence or server interaction.

The display deliberately says Reserved locally. A changed DOM is not evidence that a booking was saved, that another user sees it, or that a server accepted it.

Source file and live page

0 / 5 points

A page was loaded from a file containing <p id="status">Waiting</p>. A button handler sets that paragraph’s textContent to Finished. No storage or request is used. What should you expect after inspecting the live DOM and then opening the unchanged file again?

Attributes and properties

An attribute is part of markup, such as id="room-status". A property belongs to a DOM object, such as button.disabled or an input’s current value. Some properties reflect attributes, but they are not interchangeable in every case. An input’s value attribute describes its initial/default value; its value property reflects what the user is currently editing.

That distinction becomes important when a form starts with a value and the user changes it. Reading the original HTML is not necessarily reading the current input.

Use textContent when inserting plain text. Do not reach for innerHTML merely because a string needs displaying: interpreting a string as markup changes both the behavior and the trust boundary. We return to untrusted content in Part 5.

An edited input

0 / 5 points

An input was created with <input id="name" value="Sam">. The user replaces its text with Noor. A script needs the current text, not the initial default. Which value should it read?

Events connect interaction to a program

addEventListener registers a function; it does not call the function immediately. The browser later invokes it for an event. In the experiment, that event changes two DOM properties.

Events can move through ancestors in the document tree. The element where an event began and the element whose listener is currently running need not be identical. For now, register the listener on the element whose behavior you control and keep the change easy to follow.

Figure 2 shows the important sequence from an interaction to its visible result.

Course diagram
Figure 2. An event handler connects a user interaction to a document change and visible result.

The DOM event model explains this independently of Svelte.

Reading progress

We have changed the DOM after a button click. Now use the same mechanism to show remaining reading sections. The exercise supplies a small standalone HTML page, so there is no Svelte setup to add yet. Its handout specifies the visible text, button behavior and the one file to submit.

Keep a local reading count

0 / 15 points

Task

Complete the supplied standalone index.html. Keep it separate from the SvelteKit application: it is not a replacement for client/src/app.html.

Requirements

The document has a heading Reading progress, a paragraph with id="progress", and a native button named Read one section. Initially the paragraph reads 3 sections remaining. Each activation decreases the remaining count by one. Display 2 sections remaining, then 1 section remaining. At zero, display Reading complete and disable the button. Further activations cannot make the count negative. Opening a fresh document starts at three again.

Keep the paragraph and button available through their existing identifiers. Use text rather than interpreting user-visible strings as HTML. Do not add Svelte, network requests, or browser storage. Check the interaction with a keyboard too.

Submit

Submit only index.html at the submission root. The grader opens the standalone document in Playwright; this editor has no live Run preview. Initial output earns 5 points, the complete click sequence 5, and the terminal state and fresh-document reset 5. A changed document is not evidence of a saved server-side record.

What a framework changes

With direct DOM code, you are responsible for keeping program values and document values synchronized. If several controls display the same quantity, forgetting one update can make the interface contradict itself.

Svelte lets us describe output in terms of state. We change the state and the framework updates the relevant DOM. It does not eliminate HTML semantics, browser events, focus, or network boundaries.

Avoid mixing two owners for the same UI. If a Svelte template controls a paragraph, do not also update that paragraph through document.querySelector as the normal state-management strategy. Framework code may later overwrite the manual change. Use developer tools to inspect the DOM; use the component’s state to control it.

Check Your Understanding

  1. How can the DOM differ from the HTML originally received or opened?
  2. Why can a DOM node count differ from an element count?
  3. What is the difference between an input’s initial attribute and its current value property?
  4. Why is a native button preferable to a clickable paragraph here?
  5. What does the local room-status change fail to demonstrate?