HTML and the DOM — Page Structure

Status: Active Last Updated: 2026-08-29 Category: Web — HTML Prerequisites: Web basics Tags: html, dom, semantic, accessibility, forms, a11y

Summary

HTML (HyperText Markup Language) describes the structure of a web page. The browser parses HTML into a DOM (Document Object Model) — a tree of nodes that JavaScript can manipulate. This article covers semantic HTML, the DOM structure, accessibility basics, and forms.

What You'll Learn

Table of Contents

  1. Architectural Overview & Core Schema
  2. Deep Dive & Implementation
  3. Anti-Patterns & Common Pitfalls
  4. Independent Challenge
  5. Consolidation & Key Invariants
  6. Next Steps

1. Architectural Overview & Core Schema

Foundational theory, system mechanics, and high-level design.

HTML Structure

Every HTML page has the same skeleton:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Page Title</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
    <script src="app.js"></script>
</body>
</html>

The <head> contains metadata (title, character set, stylesheets). The <body> contains the visible content.


Semantic HTML5

HTML5 introduced elements that describe what content means, not just how it looks:

Element Purpose
<header> Introductory content (page header, article header)
<nav> Navigation links
<main> Main content of the page
<article> Self-contained piece (blog post, news article)
<section> Thematic grouping of content
<aside> Tangentially related content (sidebar)
<footer> Footer content
<h1> to <h6> Headings (only one <h1> per page)

Using semantic elements helps:


2. Deep Dive & Implementation

Technical implementation, fully commented code blocks, and step-by-step logic.

Guided Checkpoint

Verify the DOM tree structure of a page in the browser console:

// Open DevTools Elements panel and run in console:
const body = document.querySelector('main');
console.log('Direct children:', body.children.length);
console.log('All child nodes:', body.childNodes.length);
console.log('First element child tag:', body.firstElementChild?.tagName);
console.log('Parent tag:', body.parentElement?.tagName);

// Navigate: get all <section> elements and inspect their hierarchy
document.querySelectorAll('section').forEach((sec, i) => {
  console.log(`Section ${i + 1}: "${sec.id || '(no id)'}" — ${sec.children.length} children`);
});

Deep Dive — The DOM Tree

The DOM organizes nodes hierarchically: Document<html><head> / <body>. Each tag is an Element node; text between tags is a Text node; comments are Comment nodes. Traversal uses parent, child, sibling links:

const nav = document.querySelector('nav');
nav.children;           // HTMLCollection of direct child elements
nav.childNodes;         // all children including text nodes
nav.firstElementChild;  // skip text/comment
nav.parentElement;      // go up
nav.nextElementSibling; // sibling

Deep Dive — Accessibility Basics

Accessibility (a11y) ensures content is perceivable, operable, understandable, and robust. Use semantic tags: <nav>, <main>, <article>, <section>, <aside>, <footer>, <header>. Add alt text to images describing content, not filenames. Label form inputs with <label for="id"> or wrap inputs inside <label>. Use aria-label, aria-describedby, aria-expanded, and role when semantics aren't enough.

Keyboard navigation: all interactive elements must be reachable via Tab (focusable by default: <a>, <button>, <input>). Use tabindex="0" to add custom widgets, tabindex="-1" to remove from tab order. Manage focus when opening modals (element.focus()). Test with a screen reader (NVDA, VoiceOver, TalkBack) and keyboard-only navigation.

Deep Dive — Forms and Validation

Forms collect user input and submit data. Every interactive control needs a name (sent with form) and id (linked by label). Use required, min, max, pattern, type="email", type="url" for native validation. Validate server-side — client-side validation is for UX only.

<form action="/signup" method="POST" novalidate>
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>
  <button type="submit">Sign Up</button>
</form>

Handle submit events with addEventListener('submit', handler). Call event.preventDefault() to stop default navigation, gather data with new FormData(form), and send via fetch().

3. Anti-Patterns & Common Pitfalls

Documented failure modes and how to detect/prevent them.

Using <div> and <span> for everything (no semantics)

<div class="header"> and <span class="nav"> carry no meaning to screen readers or search engines. A <div> with class "nav" is invisible to assistive technology — a blind user cannot find the navigation landmarks. <span> for interactive elements removes keyboard focus by default.

Fix: Use semantic elements: <header>, <nav>, <main>, <article>, <section>, <aside>, <footer>. Reserve <div> for pure layout grouping where no semantics apply.

Missing or meaningless alt text on images

alt="" (empty) is correct for decorative images. But alt="image1.jpg" or no alt attribute at all is harmful — screen readers announce the filename, and the image provides zero context. A missing alt on a meaningful image is a WCAG failure.

Fix: Write alt text that conveys the purpose of the image: what information does it convey, or what action does it enable? alt="Company logo" for a logo, alt="Screenshot of the dashboard" for a tutorial image.

Unclosed or unquoted HTML attributes

Omitting closing > on tags or leaving attribute values unquoted creates a lenient-browser-tolerates-it-but-wrongly-parsed situation. <input type=text required> may work, but <div class=my-class> followed by <div class="other"> can cause attribute bleed. Browsers apply error recovery that produces unpredictable DOM trees.

Fix: Always quote attribute values: type="text", class="card". Always close void elements: <img ... /> or <img ...> (both valid in HTML5). Use an HTML validator (W3C validator or the browser's built-in parser warnings) to catch these.

Forms without associated <label> elements

An <input> without a <label> is inaccessible: a sighted user sees a placeholder (which disappears when typing) but a screen reader has no accessible name for the input. The placeholder attribute is never a substitute for <label>.

Fix:

<!-- WRONG -->
<input type="email" placeholder="Enter email" />

<!-- CORRECT -->
<label for="email-input">Email address</label>
<input id="email-input" type="email" name="email" />

Relying on innerHTML with user-supplied content (XSS)

element.innerHTML = userInput parses userInput as HTML. Any <script> tag or inline event handler (onerror, onclick) embedded in userInput executes in the browser. This is a primary XSS attack vector.

Fix: Never pass untrusted input to innerHTML. Use textContent for plain text:

// Safe — treats userInput as literal text
const span = document.createElement('span');
span.textContent = userInput; // HTML chars escaped automatically

Mixing structure and style in HTML attributes

Using inline style attributes, width/height on <img>, or align attributes mixes presentation into structure. This makes CSS specificity issues worse and prevents reuse.

Fix: Keep all styling in CSS (class selectors, custom properties). Use CSS width/height on <img> via classes. Keep HTML for semantics only.

4. Independent Challenge

A problem to solve without step-by-step guidance; promotes synthesis.

Build an accessible, keyboard-navigable comment thread from scratch. Requirements:

  1. Write the HTML using only semantic elements — no <div> for structural pieces
  2. Each comment must have: author name, timestamp, body text, a "Reply" button, and a "Delete" button
  3. The "Delete" button must be keyboard-accessible and announce its action via aria-label
  4. Wire up the reply and delete buttons using event delegation on the container (not one listener per button)
  5. Add required and appropriate type attributes to any form fields in the reply form
  6. Validate that the page passes an accessibility audit (install the Axe DevTools browser extension and fix all reported issues)

Constraints: No frameworks. Pure HTML + browser JS. One event listener for all comments.

5. Consolidation & Key Invariants

Bullet-list summary of the must-remember rules.

6. Next Steps

Sequenced links to dependent lessons, deeper dives, or production guides.


Change Log

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse