Code

The interactive card — the stopPropagation trap

Now we make the card interactive. In the code column — the updated js/components/card.js: it gains createFavButton (the ♡ button) and a click on the whole card that opens the detail view. Notice the card doesn't know what "open" or "save" actually do — the caller passes handlers in (onOpen, onToggleFavorite). Real state arrives in lesson 8 and a real router in lesson 14; a component that takes callbacks needs neither. Today the point is the event wiring.

Here's the classic bubbling trap. We have two click targets, one inside the other:

card.addEventListener('click', () => {
  if (onOpen) onOpen(pic);      // the whole card
});

btn.addEventListener('click', (e) => {
  e.stopPropagation();          // ← WITHOUT THIS, clicking ♡ ALSO opens the detail view
  if (onToggleFavorite) onToggleFavorite(pic);
});

Trap 1 (stopPropagation). You click ♡. The event fires on the button — but bubbles up to the card, so the card's listener fires too. Result: you save the picture AND the detail view opens. To the user, ♡ "throws" them somewhere. e.stopPropagation() stops the bubbling — the card's listener no longer fires. A real bug, easy to miss until you click exactly the ♡.

Trap 2 (preventDefault). Forms (settings, contact — lessons 11–12) have the same problem in another form:

form.addEventListener('submit', (e) => {
  e.preventDefault();           // ← WITHOUT THIS, the page RELOADS and loses everything
  handleSubmit();
});

A form's default submit behavior is to send the data and reload the page. In a single-page app we never want that: a reload wipes all state. e.preventDefault() stops the browser's default, and we handle the data ourselves.

Verify. Wire the card with temporary handlers — createCard(pic, { onOpen: p => console.log('open', p.title), onToggleFavorite: p => console.log('fav', p.title) }) — then:

1. Click the card (not ♡) → the detail view opens (or logs).
2. Click ♡ → it only saves/removes; the detail view does NOT open.
3. Comment out e.stopPropagation() → now ♡ does BOTH. Restore it — fixed.

Gotcha (recap). stopPropagation stops the event's journey through the DOM (child → parent). preventDefault stops the browser's default action (submit, link). Different problems — don't swap them.