Code

js/components/card.js — the card by hand

Now — the card. In the code column, js/components/card.js: createCard(pic) takes one picture's data and returns a ready <article> element.

Read it as a recipe: create an article, put a header with an img inside it, then a body with a title and date. Every node is createElement, configured, and appendChild. The function returns the card but never places it — where it goes is the caller's decision (in lesson 5, the gallery grid).

Three details worth noticing:

  • img.alt = pic.title — a real alt text. Screen readers read it; this is accessibility, not decoration.
  • img.loading = 'lazy' — the browser only downloads the image when it's near the viewport. A free performance win (more in lesson 5).
  • title.textContent = pic.title — the title comes from the API, so textContent, not innerHTML.

Verify. Temporarily, in main.js, create one card with fake data and put it on the page:

import { createCard } from './components/card.js';
const demo = {
  url: 'https://apod.nasa.gov/apod/image/2312/… .jpg',
  title: 'The Andromeda Galaxy',
  date: '2026-07-11',
};
document.getElementById('app').appendChild(createCard(demo));

Serve it (python -m http.server) and open it — you should see a card with the image, title and date.

Gotcha (XSS). Imagine setting the title through innerHTML:

title.innerHTML = pic.title;   // ❌ dangerous

If some title (from an API or a user) were <img src=x onerror="alert(document.cookie)">, the browser would execute it — someone else's code on your page. With textContent, that same text is just shown as characters. Always put dynamic text in via textContent. Reserve innerHTML for safe HTML you wrote yourself.