Module 4

Tables, Media & Embedding

Data Tables • Responsive Images • Audio & Video • Iframes

πŸ“Š Beyond Text: Rich Content on the Web

So far, every page you have built uses text: headings, paragraphs, lists, links, and forms. But the web is much richer than that. Spreadsheets become data tables. Photos adapt to every screen with responsive images. Podcasts play with <audio>. Lectures stream with <video>. And entire maps, social posts, and third-party apps appear via <iframe>.

This module teaches you every HTML element for non-text content. Tables that screen readers can navigate. Images that load the right size on every device. Media that plays natively in the browser. And embedded content that stays secure.

πŸ’‘
The Newspaper Analogy: A newspaper is not just columns of text. It has data tables (stock prices, sports scores), photographs, sometimes a pull-out map, and ads from other companies. A web page works the same way β€” HTML gives you dedicated elements for each type of content, so the browser knows exactly how to present it.

🎯 By the end of this module, you will:

  • Build accessible data tables with <caption>, <thead>, <tbody>, and scope
  • Merge cells with colspan and rowspan
  • Serve the right image size with srcset, sizes, and <picture>
  • Embed audio and video with native HTML controls
  • Add subtitles to video with the <track> element
  • Embed external content safely with <iframe> and sandbox

πŸ“‹ HTML Tables

A table is a grid of data organized into rows and columns. HTML gives you dedicated elements for this: <table> wraps the whole thing, <tr> creates a row, and <td> creates a data cell.

⚠️
Tables are for DATA, not layout. In the early web (1990s–2000s), developers used tables to create page layouts. This is now considered a serious anti-pattern. Tables should only contain tabular data β€” information that belongs in rows and columns (schedules, statistics, comparisons). For layout, use CSS (Flexbox, Grid).

Basic Table Structure

Three elements build every table:

Table Building Blocks

1 <table> β€” The container. Everything goes inside this element.
2 <tr> β€” Table Row. Each <tr> is one horizontal row.
3 <td> β€” Table Data cell. One piece of data inside a row.
<table>
  <tr>
    <td>Alice</td>
    <td>92</td>
    <td>A</td>
  </tr>
  <tr>
    <td>Bob</td>
    <td>78</td>
    <td>B+</td>
  </tr>
</table>

Table Headers: <th>

The first row usually contains column headings. Use <th> instead of <td> β€” the browser renders them bold and centered by default, and screen readers announce them as headers.

<table>
  <tr>
    <th>Student</th>
    <th>Score</th>
    <th>Grade</th>
  </tr>
  <tr>
    <td>Alice</td>
    <td>92</td>
    <td>A</td>
  </tr>
  <tr>
    <td>Bob</td>
    <td>78</td>
    <td>B+</td>
  </tr>
</table>
πŸ’‘
Why <th> matters: Screen readers use <th> to announce column context. When a blind user navigates to "92", the screen reader says "Score: 92" β€” but only if you used <th>. With plain <td>, it just says "92" with no context.

πŸ—οΈ Advanced Tables

Real-world tables need more than rows and cells. A good data table has a caption (title), header/body/footer sections, cells that span multiple columns or rows, and scope attributes for accessibility.

Caption: The Table Title

The <caption> element gives the table an accessible, visible title. It must be the first child of <table>. Screen readers announce it before reading the table data.

<table>
  <caption>Q3 2024 Sales Report</caption>
  <tr>
    <th>Region</th>
    <th>Revenue</th>
  </tr>
  <!-- rows... -->
</table>

Table Sections: thead, tbody, tfoot

Group your rows into logical sections. This helps the browser (it can scroll the body while keeping the header fixed), helps CSS (you can style each section differently), and helps screen readers (they know which rows are headers vs data).

Table Section Elements

1 <thead> β€” Wraps the header row(s). Contains <tr> with <th> cells.
2 <tbody> β€” Wraps the data rows. The main content of the table.
3 <tfoot> β€” Wraps the footer row(s). Totals, averages, summaries.
<table>
  <caption>Monthly Expenses</caption>
  <thead>
    <tr>
      <th scope="col">Category</th>
      <th scope="col">January</th>
      <th scope="col">February</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Rent</th>
      <td>$1200</td>
      <td>$1200</td>
    </tr>
    <tr>
      <th scope="row">Food</th>
      <td>$350</td>
      <td>$420</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row">Total</th>
      <td>$1550</td>
      <td>$1620</td>
    </tr>
  </tfoot>
</table>

Spanning: colspan & rowspan

Sometimes a cell needs to stretch across multiple columns or rows. Use colspan to merge horizontally and rowspan to merge vertically.

↔️ colspan β€” Merge Columns
<tr>
  <td colspan="3">Spans 3 columns</td>
</tr>

The cell takes the width of 3 normal cells. The row needs fewer <td> elements to compensate.

↕️ rowspan β€” Merge Rows
<td rowspan="2">
  Spans 2 rows
</td>

The cell stretches down into the next row. The row below needs one fewer <td> in that column.

Scope: Accessibility for Complex Tables

When a sighted person reads a table, they glance at the top row and left column to understand what each cell means. A screen reader can't "glance" β€” it reads cells one by one. The scope attribute is how you tell it which header belongs to which cells.

There are two values: scope="col" means "I am a header for all the cells below me in this column", and scope="row" means "I am a header for all the cells to the right of me in this row".

scope="col"
Name ↓ Alice ↓ Bob

"I label everything below me"

scope="row"
Rent β†’ $1200 β†’ $1200

"I label everything to my right"

Why does this matter? Consider this table β€” a screen reader user navigates to the highlighted cell. What does it mean?

Monthly Expenses
Category January February
Rent $1200 $1200
Food $350 $420
Total $1550 $1620
HTML
<table>
  <caption>Monthly Expenses</caption>
  <thead>
    <tr>
      <th scope="col">Category</th>
      <th scope="col">January</th>
      <th scope="col">February</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Rent</th>
      <td>$1200</td>
      <td>$1200</td>
    </tr>
    <tr>
      <th scope="row">Food</th>
      <td>$350</td>
      <td>$420</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row">Total</th>
      <td>$1550</td>
      <td>$1620</td>
    </tr>
  </tfoot>
</table>

☝️ A screen reader lands on the highlighted cell. What should it announce?

πŸ”‡ Without scope β€” Screen reader says:
β†’ "table, row 2, column 2: 1200"
πŸ€” 1200 of what? Which month? The user has to manually navigate up and left to figure it out.
πŸ”Š With scope β€” Screen reader says:
β†’ "January, Rent: 1200"
βœ… The browser automatically connects the cell to its column header (January) and row header (Rent). No guessing.
πŸ“ Simple Rule

Every <th> in a <thead> gets scope="col". Every <th> that starts a data row gets scope="row". That's it β€” two values, always on <th>, never on <td>.

Anatomy of an Accessible Table
<caption> Visible title β€” first child of table
<thead>
thscope="col" thscope="col" thscope="col"
<tbody>
thscope="row" td td
thscope="row" td td
<tfoot>
thscope="row" td td

πŸ–ΌοΈ Responsive Images

A basic <img> loads the same file on every device. A 2000px hero image downloads fully on a 320px phone β€” wasting bandwidth, slowing the page, and draining battery. Responsive images let the browser choose the best file for the current screen.

⚠️
File Paths β€” The Golden Rule

Never use absolute local paths like /Users/name/Desktop/photo.jpg or C:\Users\name\photo.jpg in your HTML. They only work on your computer β€” deploy to a server and everything breaks because the server has a completely different file structure. Use only:

  • Relative paths β†’ images/photo.jpg β€” relative to your HTML file's location
  • Full external URLs β†’ https://example.com/photo.jpg β€” works from anywhere on any server

In this module, code examples use full external URLs so you can test them directly with "Try it β†’".

srcset: Resolution Switching

The srcset attribute provides a list of image files with their widths. The browser picks the best one based on the viewport and pixel density.

<img
  src="https://picsum.photos/id/29/800/600"
  srcset="https://picsum.photos/id/29/400/300 400w,
         https://picsum.photos/id/29/800/600 800w,
         https://picsum.photos/id/29/1200/900 1200w"
  sizes="(max-width: 600px) 100vw,
        (max-width: 1000px) 50vw,
        33vw"
  alt="A mountain landscape"
>
πŸ–₯️ Live Demo β€” Resize your browser to see the image change
Mountain landscape β€” the browser loads a different resolution depending on your screen width

↑ This image uses srcset with 3 sizes: 400px, 800px, and 1200px. Open DevTools β†’ Network tab and resize to see the browser pick the optimal file.

How srcset + sizes Work Together

1 src β€” The fallback image for browsers that don't support srcset.
2 srcset β€” A comma-separated list of images with their intrinsic widths (e.g. 400w means the file is 400px wide).
3 sizes β€” Tells the browser how wide the image will be in the layout. Media conditions map viewport ranges to image display sizes.

The <picture> Element: Art Direction

Sometimes you don't just want a smaller version β€” you want a different image entirely. A wide landscape shot for desktop, a square crop for mobile. The <picture> element gives you full control.

<picture>
  <source
    media="(min-width: 800px)"
    srcset="https://picsum.photos/id/1015/1200/400"
  >
  <source
    media="(min-width: 400px)"
    srcset="https://picsum.photos/id/1015/600/600"
  >
  <img
    src="https://picsum.photos/id/1015/400/600"
    alt="Product showcase"
  >
</picture>
🎨 Live Art Direction Demo β€” Resize to see a completely different crop
River flowing between mountains β€” different crop on each screen size

↑ Wide panoramic on desktop, square on tablet, tall portrait on mobile. Three different crops of the same scene β€” that is art direction with <picture>.

The browser evaluates <source> elements from top to bottom and uses the first one whose media query matches. The <img> at the bottom is the mandatory fallback β€” <picture> does not work without it.

Which Image Approach?
Do you need different image crops for different screens?
Yes
Use <picture> with <source media="..."> β€” full art direction control
No, same image
Do you need different file sizes for performance?
Yes
Use <img srcset="..." sizes="..."> β€” browser picks the best size
No
Use a plain <img src="..." alt="..."> β€” simple and effective
πŸ’‘
Modern formats: Use <picture> to serve modern formats like WebP or AVIF with a JPEG fallback: <source type="image/webp" srcset="photo.webp">. These formats are 25–50% smaller with the same quality.

πŸ”Š Audio

The <audio> element embeds sound directly in the page β€” no plugins, no Flash, no JavaScript. Add the controls attribute and the browser shows a native play button, progress bar, and volume slider.

<audio controls>
  <source src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>

Audio Attributes

1 controls β€” Shows play/pause, progress, and volume. Always include this β€” without it, the audio is invisible and users cannot interact.
2 autoplay β€” Starts playing immediately. Browsers often block this unless the audio is also muted. Avoid it β€” unexpected sound annoys users.
3 loop β€” Restarts from the beginning when it reaches the end.
4 muted β€” Starts with volume at zero. Required for autoplay to work in most browsers.
5 preload β€” Hint to the browser: "none" (don't load), "metadata" (load duration only), or "auto" (load the whole file). Default varies by browser.

Audio Formats

Not every browser supports every format. Provide multiple <source> elements as fallbacks β€” the browser uses the first one it supports.

🎡 MP3 audio/mpeg Universal support. Best default choice.
🎢 OGG Vorbis audio/ogg Open format. Firefox, Chrome, Edge. No Safari.
🎼 WAV audio/wav Uncompressed. Huge files. Use for short sound effects only.
πŸ”ˆ AAC audio/aac Apple's format. Safari, Chrome, Edge. Better compression than MP3.

🎬 Video

The <video> element works like <audio> but with a visual component. It supports multiple sources, a poster image (thumbnail), and β€” critically β€” the <track> element for subtitles and captions.

<video
  controls
  width="640"
  height="360"
  poster="https://picsum.photos/id/180/640/360"
>
  <source src="https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4" type="video/mp4">
  Your browser does not support the video element.
</video>

Video Attributes

1 controls β€” Play/pause, seek bar, volume, fullscreen. Always include unless you build custom controls with JavaScript.
2 poster β€” A thumbnail image shown before the video plays. Without it, the browser shows the first frame (often a black screen).
3 width / height β€” Set dimensions to prevent layout shift. The browser reserves space before the video loads.
4 autoplay + muted β€” Autoplaying video must be muted in all modern browsers. Useful for background decorative video.

Subtitles & Captions: The <track> Element

The <track> element adds text tracks (subtitles, captions, descriptions) to a video. It references a WebVTT file (.vtt) β€” a simple text format with timestamps.

<video controls width="640" height="360">
  <source src="https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4" type="video/mp4">
  <track
    src="assets/captions-en.vtt"
    kind="captions"
    srclang="en"
    label="English"
    default
  >
  <track
    src="assets/subtitles-fr.vtt"
    kind="subtitles"
    srclang="fr"
    label="FranΓ§ais"
  >
</video>
🎬 Live Demo β€” Play the video to see captions in action

↑ Click the CC button in the video controls to toggle captions. Use the settings gear to switch between English captions and French subtitles. The .vtt files are in the assets/ folder.

πŸ”€ Captions vs Subtitles
C Captions (kind="captions") β€” Include all sounds: dialogue, music, sound effects. For deaf/hard-of-hearing users.
S Subtitles (kind="subtitles") β€” Translate dialogue only into another language. Assume the viewer can hear.
πŸ“„ WebVTT Format
WEBVTT

00:00:00.500 --> 00:00:02.000
Introducing Chromecast.

00:00:02.500 --> 00:00:06.000
The easiest way to enjoy online
video and music on your TV.

00:00:06.500 --> 00:00:10.000
For when Batman's escapes
aren't quite big enough.

00:00:11.000 --> 00:00:13.000
[music] For $35.
β™Ώ
Accessibility: Captions are not optional. They help deaf users, non-native speakers, people in noisy environments, and anyone who watches with sound off. Always provide at least one caption track.
πŸŽ₯ MP4 (H.264) video/mp4 Universal support. Best default choice.
🌐 WebM (VP9) video/webm Open format. Chrome, Firefox, Edge. Smaller files.
🍎 OGV (Theora) video/ogg Legacy open format. Firefox, Chrome. Rarely needed today.

πŸͺŸ Iframes & Embedding

An <iframe> embeds an entire web page inside your page β€” like a window into another site. YouTube videos, Google Maps, CodePen demos, and social media posts all use iframes.

<iframe
  src="https://en.wikipedia.org/wiki/HTML"
  width="560"
  height="315"
  title="Wikipedia article about HTML"
  loading="lazy"
  allowfullscreen
></iframe>

Key Iframe Attributes

1 src β€” The URL of the page to embed. For YouTube, use the /embed/ URL, not the regular watch URL.
2 title β€” Required for accessibility. Screen readers use this to describe the iframe. "YouTube video player" is not enough β€” describe the content.
3 loading="lazy" β€” Defers loading until the iframe scrolls into view. Huge performance win for pages with multiple embeds.
4 allowfullscreen β€” Lets the embedded content go fullscreen (required for video players).

Embedding Google Maps

Google Maps provides an embed URL that you drop into an iframe. Go to Google Maps β†’ Share β†’ Embed a map β†’ copy the iframe code.

<iframe
  src="https://www.google.com/maps/embed?pb=!1m18..."
  width="600"
  height="450"
  style="border:0"
  allowfullscreen
  loading="lazy"
  title="Office location on Google Maps"
></iframe>

πŸ”’ Iframe Security

An iframe runs code from another website inside your page. This is a security risk. The sandbox attribute restricts what the iframe can do.

❌ No Sandbox β€” Full Access
<iframe src="...">
</iframe>

The embedded page can run scripts, submit forms, navigate your page, access cookies, and more.

βœ… Sandboxed β€” Restricted
<iframe
  src="..."
  sandbox="allow-scripts
          allow-same-origin"
></iframe>

Only explicitly allowed capabilities work. No form submission, no popups, no top-level navigation.

Sandbox Values

1 allow-scripts β€” Let the iframe run JavaScript. Needed for most embeds (YouTube, Maps).
2 allow-same-origin β€” Let the iframe access its own cookies/storage. Needed for login-based embeds.
3 allow-forms β€” Let the iframe submit forms. Only enable if the embed needs form submission.
4 allow-popups β€” Let the iframe open new windows. Use with caution.
⚠️
Security rule: Never combine allow-scripts and allow-same-origin on an iframe pointing to a domain you don't trust β€” the embedded page could remove the sandbox entirely. Only use both together for trusted sources (YouTube, Google Maps, your own domains).

✏️ Practice: Tables, Media & Embedding

Try each exercise mentally first, then reveal the solution to check your understanding.

Exercise 1: Build an Accessible Table

Create a table showing a class schedule: 3 columns (Day, Subject, Time) and 3 data rows. Include a caption, use <thead>/<tbody>, and add scope to every <th>.

Exercise 2: Choose the Right Element

For each scenario, which HTML element or attribute should you use?

1. You need a hero image that loads a 400px version on phones and a 1200px version on desktops.

2. You want a landscape photo on desktop but a portrait crop on mobile.

3. You want to embed a podcast that users can play/pause.

4. You have a lecture video and need French subtitles.

5. You need to show a comparison of quarterly sales in rows and columns.

Exercise 3: Spot the Iframe Issues

This iframe embeds a third-party widget. Find all the problems.

<iframe
  src="https://untrusted-widgets.example.com/chat"
  width="400"
  height="300"
></iframe>

Exercise 4: Fix the Broken Video

This video element has multiple problems. Find and fix them all.

<video autoplay>
  <source src="/Users/mehdi/Desktop/lecture.mp4">
</video>

πŸ“‹ Module Summary

πŸ“‹
Data Tables

<table> with <tr>, <th>, <td>. Use <caption> for a title, <thead>/<tbody>/<tfoot> for structure, and scope on every <th> for accessibility.

↔️
Cell Spanning

colspan merges cells horizontally, rowspan merges vertically. Adjust the number of cells in affected rows to compensate.

πŸ–ΌοΈ
Responsive Images

srcset + sizes for resolution switching. <picture> + <source media> for art direction. Modern formats (WebP, AVIF) via <source type>.

πŸ”Š
Audio

<audio controls> with <source> for format fallback. Always include controls. Avoid autoplay β€” browsers block it anyway.

🎬
Video & Captions

<video controls poster="..."> with <track kind="captions"> for accessibility. Set width/height to prevent layout shift.

πŸͺŸ
Iframes & Security

Embed YouTube, Maps, and third-party content with <iframe>. Always add title, loading="lazy", and sandbox for untrusted sources.

Next Module β†’

Module 5: CSS Fundamentals

How CSS works: selectors, specificity, the cascade, the box model, units, and colors. The foundation for everything visual on the web.

β†’