# The DOM

The page as a tree of objects. Finding an element, changing it, adding new ones, and reacting to a click.

Multimedia & Creative Technology · 2.1 Webcoding · Lesson 6 · version 1.0.0 · 2026-10-26

Tags: javascript, dom, html

Slides: <https://slides.mctantwerp.be/en/webcoding/06-dom>

Speaker notes: <https://slides.mctantwerp.be/en/webcoding/06-dom.notes.md>

<!-- slide 2 -->

## Agenda

1. What the DOM is
2. Selecting elements
3. Changing them
4. Adding and removing
5. When the script runs
6. Events
7. Exercises

<!-- slide 3 -->

## 1. What the DOM is

<!-- slide 4 -->

### One piece of the page

```html
<h1>Digital Nomads</h1>
<p>Likes: <span id="likes">0</span></p>
<button id="like">👍 Like</button>

<script>
  let likes = 0;
  const button = document.querySelector('#like');
  const counter = document.querySelector('#likes');

  button.addEventListener('click', function () {
    likes++;
    counter.textContent = likes;
  });
</script>
```

<!-- slide 5 -->

### HTML is text, the DOM is a tree

#### index.html

```html
<html>
<head>
  <title>My website</title>
</head>
<body>
  <header><h1>Welcome</h1></header>
  <main>
    <p class="intro">Hi there</p>
    <button id="cta">Click me</button>
  </main>
</body>
</html>
```

#### What the browser builds

The DOM tree:

- document
  - html
    - head
      - title ("My website")
    - body
      - header
        - h1 ("Welcome")
      - main
        - p.intro ("Hi there")
        - button#cta ("Click me")

<!-- slide 6 -->

### Document Object Model

- **D Document**: The page that is loaded in the tab. In JavaScript it is `document`.
- **O Object**: Every element becomes an object, with properties you can read and change: its text, its classes, its `src`.
- **M Model**: The whole page as a tree, in the memory of the browser. Change the tree and the screen follows.

<!-- slide 7 -->

### The tree

The DOM tree:

- document
  - html
    - head
      - title ("My website")
    - body
      - header
        - h1 ("Welcome")
      - main
        - p.intro ("Hi there")
        - button#cta ("Click me")

<!-- slide 8 -->

### Family

- **Parent**: the element around another one. `main` is the parent of `p.intro`.
- **Child**: an element directly inside another one. `header` and `main` are the children of `body`.
- **Sibling**: a child of the same parent. `p.intro` and `button#cta` are siblings.
- **Descendant**: anything inside, however deep. Every element in this page is a descendant of `html`.

<!-- slide 9 -->

### The browser fixes your HTML

```html
<table>
  <tr><td>Fries</td><td>€3</td></tr>
</table>

<script>
  const table = document.querySelector('table');

  console.log(table.firstElementChild);
</script>
```

<!-- slide 10 -->

### View source or Elements

#### View page source

The file as the server sent it. Whatever JavaScript does later, this stays the same.

Windows <kbd>Ctrl</kbd>+<kbd>U</kbd>, Mac <kbd>Cmd</kbd>+<kbd>Option</kbd>+<kbd>U</kbd>

#### DevTools, tab Elements

The DOM as it is right now. A change from JavaScript, or from you, shows up here live.

Windows <kbd>F12</kbd>, Mac <kbd>Cmd</kbd>+<kbd>Option</kbd>+<kbd>I</kbd>

> [!WARNING]
> A reload builds the DOM again from the file. Every change is gone.

<!-- slide 11 -->

### DIY: Walk the tree

*5 minutes*

1. Open [term1.webcoding.learn.mctantwerp.be](https://term1.webcoding.learn.mctantwerp.be/) and the DevTools, tab Elements.
2. Unfold the tree with the little arrows until you reach the first `<div class="callout-content">`. No inspect, no search.
3. Double click the text of the h3 inside it and type something else.
4. Reload the page. Where did your text go?

<!-- slide 12 -->

### document

```html
<h1>Digital Nomads</h1>
<p>Antwerp, 2026</p>

<script>
  console.log(document);
  console.log(document.body);
  console.log(document.body.firstElementChild);
  console.log(document.body.children.length);
</script>
```

<!-- slide 13 -->

## 2. Selecting elements

<!-- slide 14 -->

### querySelector

```js
const title = document.querySelector('h1');

const intro = document.querySelector('.intro');

const button = document.querySelector('#cta');
```

1. The first `h1` of the page. A tag name, like in CSS.
2. The first element with the class `intro`. A dot for a class.
3. The element with the id `cta`. A hash for an id.

<!-- slide 15 -->

### Any CSS selector

A selector lab: type a CSS selector and the elements that querySelector or querySelectorAll would give back light up in the page. Selectors to try: `li`, `.snack`, `#order`, `nav a`, `.snack.veggie`, `ul > li:nth-child(2)`, `a.active`, `h1, button`.

<!-- slide 16 -->

### querySelectorAll

```html
<ul>
  <li class="snack">Fries</li>
  <li class="snack">Frikandel</li>
  <li class="drink">Cola</li>
</ul>

<script>
  const items = document.querySelectorAll('li');
  console.log(items.length);

  items.forEach(function (item) {
    item.textContent = item.textContent + ' €3';
  });
</script>
```

<!-- slide 17 -->

### Nothing found

```html
<h1 class="title">Digital Nomads</h1>

<script>
  const title = document.querySelector('title');
  console.log(title);

  title.textContent = 'Changed';
</script>
```

> [!WARNING]
> `Cannot read properties of null` means the selector found nothing.

<!-- slide 18 -->

### The classics

| Method | Gives back | Watch out |
| --- | --- | --- |
| `getElementById('cta')` | One element or null | No hash in front of the id |
| `getElementsByClassName('snack')` | An HTMLCollection | No dot, and no forEach |
| `getElementsByTagName('li')` | An HTMLCollection | The first one is \[0\] |

> [!TIP]
> `querySelector` and `querySelectorAll` take any CSS selector and do all of this. Those are the two we write.

<!-- slide 19 -->

### A page has three elements with the class card. What does document.querySelector('.card') give back?

- A. The first card
- B. All three, in a NodeList
- C. The last card
- D. null, because there is more than one

**Answer:** A. The first card

querySelector stops at the first match, top to bottom in the page. For all three you need querySelectorAll.

<!-- slide 20 -->

### What does document.getElementById('#cta') give back, when the page has a button with id cta?

- A. The button
- B. null
- C. A SyntaxError
- D. A NodeList with the button

**Answer:** B. null

getElementById takes the id itself, without the hash. It looks for an element with the id '#cta', finds none and gives null. querySelector('#cta') would find the button.

<!-- slide 21 -->

### DIY: Select from the console

*10 minutes*

1. Open [term1.webcoding.learn.mctantwerp.be](https://term1.webcoding.learn.mctantwerp.be/) and the console in the DevTools.
2. Select the `h1`, log it with `console.log()`, and change its text.
3. Write three different selectors that find the same element.
4. Select every `li` on the page and log how many there are.

> [!TIP]
> **Select from the console**
>
> *h1*
>
> ```js
> const title = document.querySelector('h1');
> console.log(title);
> title.textContent = 'I was here';
> ```
>
> *Three selectors*
>
> For the first callout, for example. Your page might allow others.
>
> ```js
> document.querySelector('.callout-content');
> document.querySelector('div.callout-content');
> document.querySelectorAll('.callout-content')[0];
> ```
>
> *Count*
>
> ```js
> const items = document.querySelectorAll('li');
> console.log(items.length);
> ```

<!-- slide 22 -->

## 3. Changing them

<!-- slide 23 -->

### textContent

```html
<h1>Hello world</h1>
<p id="status">Loading...</p>

<script>
  const title = document.querySelector('h1');
  console.log(title.textContent);

  title.textContent = 'Hello DOM';

  const status = document.querySelector('#status');
  status.textContent = 'Ready';
</script>
```

<!-- slide 24 -->

### innerHTML

```html
<p id="one"></p>
<p id="two"></p>

<script>
  const text = 'Fries are <b>not</b> a vegetable';

  document.querySelector('#one').textContent = text;
  document.querySelector('#two').innerHTML = text;
</script>
```

<!-- slide 25 -->

### innerHTML and a visitor

```html
<p id="greeting"></p>

<script>
  const name = prompt('Your name?');
  const greeting = document.querySelector('#greeting');

  greeting.innerHTML = 'Hello ' + name;
</script>
```

*prompt() → `<img src="x" onerror="alert('Your cookies are mine')">`*

*innerHTML*

```html
<p id="greeting"></p>

<script>
  const name = prompt('Your name?');
  const greeting = document.querySelector('#greeting');

  greeting.innerHTML = 'Hello ' + name;
</script>
```

*prompt() → `<img src="x" onerror="alert('Your cookies are mine')">`*

*textContent*

```html
<p id="greeting"></p>

<script>
  const name = prompt('Your name?');
  const greeting = document.querySelector('#greeting');

  greeting.textContent = 'Hello ' + name;
</script>
```

*prompt() → `<img src="x" onerror="alert('Your cookies are mine')">`*

> [!WARNING]
> Text from a visitor goes in `textContent`. Always.

<!-- slide 26 -->

### Attributes

```html
<a href="https://www.kdg.be">KdG</a>
<button id="send">Send</button>

<script>
  const link = document.querySelector('a');
  link.href = 'https://www.mctantwerp.be';
  link.textContent = 'MCT';
  link.setAttribute('target', '_blank');
  const send = document.querySelector('#send');
  send.addEventListener('click', function () {
    send.disabled = true;
    console.log(link.getAttribute('href'));
  });
</script>
```

<!-- slide 27 -->

### classList

```html
<style>
  .dark { background: black; color: white; }
</style>
<button id="mode">Dark mode</button>
<script>
  const mode = document.querySelector('#mode');
  mode.addEventListener('click', function () {
    document.body.classList.toggle('dark');
    console.log(document.body.classList.contains('dark'));
  });
</script>
```

> [!NOTE]
> `add`, `remove`, `toggle` and `contains`. All four take the name of the class, without a dot.

<!-- slide 28 -->

### style

```js
box.style.backgroundColor = 'tomato';
box.style.width = '200px';
box.style.left = x + 'px';
```

1. `background-color` becomes `backgroundColor`. No minus signs in a property name.
2. Always a string, always with the unit. `200` on its own does nothing.
3. For values JavaScript calculates. Everything else goes in a class.

<!-- slide 29 -->

### What can an element do?

- **console.dir(element)**: In the console of a real page. Every property of the element, unfolded like an object.
- **[MDN, per element](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement)**: An image is an `HTMLImageElement`, a link an `HTMLAnchorElement`. Each has its own page.

<!-- slide 30 -->

### Which line makes the text of a paragraph red?

- A. p.style.color = 'red';
- B. p.style = red;
- C. p.color = 'red';
- D. p.style.font-color = 'red';

**Answer:** A. p.style.color = 'red';

CSS properties live on style, and the value is a string. p.color does not exist, and font-color is not CSS. With a minus sign in a property name, JavaScript would even try to subtract.

<!-- slide 31 -->

## 4. Adding and removing

<!-- slide 32 -->

### A new element

```js
const item = document.createElement('li');
item.textContent = 'Waffle';

list.append(item);
```

1. A new `li`, in memory. Not on the page yet.
2. Filled like any other element.
3. Now it is in the tree, as the last child of `list`, and on the screen.

<!-- slide 33 -->

### Where it lands

```html
<ul id="menu">
  <li>Fries</li>
</ul>

<script>
  const menu = document.querySelector('#menu');
  const last = document.createElement('li');
  last.textContent = 'Cola (append)';
  menu.append(last);

  const first = document.createElement('li');
  first.textContent = 'Soup (prepend)';
  menu.prepend(first);
</script>
```

<!-- slide 34 -->

### A list from an array

```html
<ul id="cities"></ul>

<script>
  const cities = ['Antwerp', 'Ghent', 'Mechelen', 'Lier'];
  const list = document.querySelector('#cities');

  cities.forEach(function (city) {
    const item = document.createElement('li');
    item.textContent = city;
    list.append(item);
  });
</script>
```

<!-- slide 35 -->

### remove

```html
<p class="ad">Buy now!</p>
<p>The actual article.</p>
<p class="ad">Last chance!</p>
<button id="clean">Remove the ads</button>

<script>
  const clean = document.querySelector('#clean');

  clean.addEventListener('click', function () {
    document.querySelectorAll('.ad').forEach(function (ad) {
      ad.remove();
    });
  });
</script>
```

<!-- slide 36 -->

### Walking the tree

```js
item.parentElement
item.closest('.card')
list.children

item.nextElementSibling
item.previousElementSibling
```

1. One level up.
2. Up until an ancestor matches the selector, or null.
3. The elements directly inside, as an HTMLCollection.
4. The next element with the same parent.
5. The one before.

<!-- slide 37 -->

### DIY: Build and break

*5 minutes*

1. Back on [term1.webcoding.learn.mctantwerp.be](https://term1.webcoding.learn.mctantwerp.be/), in the console.
2. Create a new `p`, give it the text "Created with JavaScript" and add it to the end of the page.
3. Remove the last `p` of the page. Which one was that?
4. Reload. Both changes are gone.

> [!TIP]
> **Build and break**
>
> ```js
> const p = document.createElement('p');
> p.textContent = 'Created with JavaScript';
> document.body.append(p);
>
> const all = document.querySelectorAll('p');
> all[all.length - 1].remove();
> ```

<!-- slide 38 -->

## 5. When the script runs

<!-- slide 39 -->

### Too early

```html
<!DOCTYPE html>
<html>
<head>
  <script>
    const title = document.querySelector('h1');
    title.textContent = 'Changed';
  </script>
</head>
<body>
  <h1>My website</h1>
</body>
</html>
```

<!-- slide 40 -->

### Three ways out

| Where | How |
| --- | --- |
| At the end of the body | `<script src="script.js"></script>` |
| **In the head, with defer** | `<script src="script.js" defer></script>` |
| Anywhere, waiting for the event | `document.addEventListener('DOMContentLoaded', ...)` |

> [!TIP]
> From today: the script tag goes in the head, with `defer`.

<!-- slide 41 -->

### defer

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <title>My website</title>
  <script src="script.js" defer></script>
</head>
<body>
  <h1>My website</h1>
</body>
</html>
```

#### script.js

```js
// The whole DOM is there when this runs
const title = document.querySelector('h1');
title.textContent = 'Changed';
```

<!-- slide 42 -->

### A script in the head, without defer, runs document.querySelector('h1'). The page has an h1. What does it give back?

- A. null
- B. The h1
- C. A SyntaxError
- D. undefined

**Answer:** A. null

When the browser runs that script, it has not read the body yet, so there is no h1 in the DOM. querySelector gives null. With defer, or with the script at the end of the body, it finds the h1.

<!-- slide 43 -->

## 6. Events

<!-- slide 44 -->

### addEventListener

```html
<button id="hello">Say hello</button>
<p id="output"></p>

<script>
  const button = document.querySelector('#hello');
  const output = document.querySelector('#output');

  button.addEventListener('click', function () {
    output.textContent = 'Hello!';
  });
</script>
```

<!-- slide 45 -->

### Hand it over, again

```html
<button id="hello">Say hello</button>

<script>
  function sayHello() {
    console.log('Hello!');
  }

  const button = document.querySelector('#hello');
  button.addEventListener('click', sayHello);
</script>
```

*named*

```html
<button id="hello">Say hello</button>

<script>
  function sayHello() {
    console.log('Hello!');
  }

  const button = document.querySelector('#hello');
  button.addEventListener('click', sayHello);
</script>
```

*with brackets*

```html
<button id="hello">Say hello</button>

<script>
  function sayHello() {
    console.log('Hello!');
  }

  const button = document.querySelector('#hello');
  button.addEventListener('click', sayHello());
</script>
```

<!-- slide 46 -->

### Events you will use

| Event | Fires when |
| --- | --- |
| [click](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event) | The element is clicked, or a button is pressed with Enter or Space |
| [mouseenter / mouseleave](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event) | The mouse moves onto the element, or off it |
| [mousemove](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) | The mouse moves over it, many times per second |
| [keydown](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event) | A key goes down |
| [input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) | The value of a field changes, with every key |
| [contextmenu](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event) | The right mouse button, just before the menu opens |

<!-- slide 47 -->

### The event object

```html
<div id="pad" style="height: 200px; background: lightgrey">
  Move over me
</div>
<p id="where">...</p>

<script>
  const pad = document.querySelector('#pad');
  const where = document.querySelector('#where');

  pad.addEventListener('mousemove', function (event) {
    where.textContent = event.clientX + ', ' + event.clientY;
  });
</script>
```

<!-- slide 48 -->

### event.target

```html
<button class="snack">Fries</button>
<button class="snack">Waffle</button>
<button class="snack">Smoothie</button>
<p id="order"></p>

<script>
  const order = document.querySelector('#order');
  const snacks = document.querySelectorAll('.snack');
  snacks.forEach(function (button) {
    button.addEventListener('click', function (event) {
      order.textContent = event.target.textContent;
    });
  });
</script>
```

<!-- slide 49 -->

### preventDefault

```html
<a href="https://www.kdg.be" id="link">To KdG</a>

<script>
  const link = document.querySelector('#link');

  link.addEventListener('click', function (event) {
    event.preventDefault();
    console.log('Not today');
  });
</script>
```

<!-- slide 50 -->

### button.addEventListener('click', count()); What happens?

- A. count runs once while the page loads, and a click does nothing
- B. count runs on every click
- C. count runs twice on every click
- D. A SyntaxError, the page does not load

**Answer:** A. count runs once while the page loads, and a click does nothing

The brackets call count on the spot, and addEventListener gets whatever count gave back, usually undefined. Hand over the function itself: addEventListener('click', count).

<!-- slide 51 -->

### Select, listen, change

```js
// 1. Select
const button = document.querySelector('#like');
const counter = document.querySelector('#likes');
let likes = 0;

// 2. Listen
button.addEventListener('click', function () {
  // 3. Change
  likes++;
  counter.textContent = likes;
});
```

1. Find the elements you need, once, at the top.
2. Say which event you wait for, on which element.
3. Change the DOM, inside the function, at the moment it happens.

<!-- slide 52 -->

**Select, listen, change.**

Every exercise after this one, every form, every library and every bit of jQuery is these three steps with other names.

<!-- slide 53 -->

## 7. Exercises

<!-- slide 54 -->

### How it works

- One folder per exercise, named after it: `1-1-change-text`. Inside an `index.html` and a `script.js`, linked with `defer`.
- Clean HTML, also in a JavaScript lesson. Indented, with a title, no inline styles.
- There is no one right solution. **Every exercise can be solved in twenty ways.**
- Google and AI are fine to get unstuck. A complete solution from them teaches you nothing.
- The browser window next to every exercise is the expected result, and it works. The 😵 hides a solution.

<!-- slide 55 -->

### ⭐ 1.1 and 1.2: Change it

1. Select an element and change its text, as soon as the page loads.
2. Change the background colour of an element.

> [!TIP]
> **Change it**
>
> *index.html*
>
> ```html
> <!DOCTYPE html>
> <html lang="en">
> <head>
>   <title>1.1 Change it</title>
>   <script src="script.js" defer></script>
> </head>
> <body>
>   <h1 id="title">Hello world</h1>
>   <p>The heading and the colour change.</p>
> </body>
> </html>
> ```
>
> *script.js*
>
> ```js
> const title = document.querySelector('#title');
> title.textContent = 'Changed by JavaScript';
>
> document.body.style.backgroundColor = 'gold';
> ```

Expected result: when the page loads, the heading reads "Changed by JavaScript" and the background of the page changes colour.

<!-- slide 56 -->

### ⭐ 1.3: Pick a font

1. Three buttons. Each one changes the font of every paragraph on the page.

> [!TIP]
> **Pick a font**
>
> *index.html*
>
> ```html
> <button id="serif">Serif</button>
> <button id="sans">Sans</button>
> <button id="mono">Mono</button>
>
> <p>Antwerp has more than 500 000 inhabitants.</p>
> <p>The cathedral took almost 170 years to build.</p>
> ```
>
> *script.js*
>
> ```js
> const paragraphs = document.querySelectorAll('p');
>
> function setFont(font) {
>   paragraphs.forEach(function (p) {
>     p.style.fontFamily = font;
>   });
> }
>
> document.querySelector('#serif').addEventListener('click', function () {
>   setFont('Georgia, serif');
> });
> document.querySelector('#sans').addEventListener('click', function () {
>   setFont('Arial, sans-serif');
> });
> document.querySelector('#mono').addEventListener('click', function () {
>   setFont('Courier New, monospace');
> });
> ```

Expected result: three buttons, Serif, Sans and Mono. Each click puts both paragraphs in that font.

<!-- slide 57 -->

### ⭐ 1.4: Random text

1. A click on a button shows a random text on the page. The texts come from an array.

> [!TIP]
> **Random text**
>
> ```js
> const quotes = [
>   'It works on my machine.',
>   'Have you tried turning it off and on again?',
>   'console.log is my debugger.',
> ];
> const output = document.querySelector('#quote');
>
> document.querySelector('#inspire').addEventListener('click', function () {
>   const index = Math.floor(Math.random() * quotes.length);
>   output.textContent = quotes[index];
> });
> ```

Expected result: a button "Inspire me". Every click shows a random quote from a list under it.

<!-- slide 58 -->

### ⭐ 1.5: Bigger

1. An image on the page. A click makes it bigger, for example from 200 to 800 pixels wide.
2. Extra: the next click makes it small again.

> [!TIP]
> **Bigger**
>
> *width*
>
> ```js
> const photo = document.querySelector('#photo');
>
> photo.addEventListener('click', function () {
>   if (photo.width === 200) {
>     photo.width = 800;
>   } else {
>     photo.width = 200;
>   }
> });
> ```
>
> *classList*
>
> *style.css*
>
> ```css
> #photo { width: 200px; transition: width 0.3s; }
> #photo.big { width: 800px; }
> ```
>
> *script.js*
>
> ```js
> const photo = document.querySelector('#photo');
>
> photo.addEventListener('click', function () {
>   photo.classList.toggle('big');
> });
> ```

Expected result: a small image. A click makes it a lot wider, the next click small again.

<!-- slide 59 -->

### Next up

- **29/10** The DOM: Today
- **16/11** DOM in practice: Monday 13:30, HO-A.01.39
- **23/11** Forms
- **30/11** JavaScript libraries

- Level one of the exercises finished. **Every slide of the practice session assumes it.**

## Glossary

- **DOM**: Document Object Model. The page as the browser keeps it in memory: a tree with an object for every element. JavaScript reads and changes the DOM, never the HTML file itself. (slide 6)
- **Node**: One piece of the DOM tree. An element is a node, and so is the text inside it. (slide 7)
- **Parent, child, sibling**: How nodes in the tree relate. The element around another one is its parent, the elements directly inside are its children, and children of the same parent are siblings. (slide 8)
- **DevTools**: The developer tools of the browser, F12 or Cmd+Option+I. The Elements tab shows the live DOM, the Console runs JavaScript on the page. (slide 10)
- **querySelector**: Gives back the first element that matches a CSS selector, or null when nothing matches: document.querySelector(".intro"). (slide 14)
- **querySelectorAll**: Gives back every element that matches a CSS selector, as a NodeList. Loop over it with forEach, ask its length. (slide 16)
- **NodeList**: The list querySelectorAll gives back. It looks like an array and has forEach and length, but no map or filter. Array.from turns it into a real array. (slide 16)
- **null**: What querySelector gives back when no element matches. Touch a property of it and you get "Cannot read properties of null". (slide 17)
- **textContent**: The text inside an element. Read it, or assign a string to replace everything inside the element with that text. HTML in it shows up as text. (slide 23)
- **innerHTML**: The HTML inside an element, as a string. Assign to it and the browser builds new elements from it. Never with input from a visitor. (slide 24)
- **classList**: The classes of an element, with add, remove, toggle and contains. The cleanest way to change how an element looks: the CSS stays in the stylesheet. (slide 27)
- **createElement**: Makes a new element that is not on the page yet: document.createElement("li"). It only shows up after append or prepend. (slide 32)
- **defer**: An attribute on a script tag in the head: the browser downloads the file right away, and runs it once the whole page is read. The DOM is complete by then. (slide 40)
- **DOMContentLoaded**: The event that fires when the browser has read the whole HTML and built the DOM. Old scripts wrap everything in it, defer makes it unnecessary. (slide 40)
- **addEventListener**: Tells an element which function to run when an event happens on it: button.addEventListener("click", sayHello). Replaces the onclick attribute. (slide 44)
- **Event object**: The object the browser hands your listener when the event fires. It knows what happened: event.target, event.type, event.key, event.clientX. (slide 47)
- **preventDefault**: A method of the event object that stops what the browser would do on its own: follow a link, send a form, open the right click menu. (slide 49)

## Links and sources

### In the deck

- [term1.webcoding.learn.mctantwerp.be](https://term1.webcoding.learn.mctantwerp.be/) (slide 11, slide 21, slide 37)
- [MDN, per element](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement) (slide 29)
- [click](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event) (slide 46)
- [mouseenter / mouseleave](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event) (slide 46)
- [mousemove](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) (slide 46)
- [keydown](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event) (slide 46)
- [input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) (slide 46)
- [contextmenu](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event) (slide 46)
- [kdgmt.be/discord](https://kdgmt.be/discord) (slide 60)

### Sources

- [Introduction to the DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction): What the DOM is and how it relates to your HTML
- [Locating DOM elements using selectors](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Locating_DOM_elements_using_selectors)
- [Document: querySelector() method](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
- [Document: querySelectorAll() method](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)
- [Node: textContent property](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
- [Element: innerHTML property](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML): The security section explains the img onerror trick
- [Element: classList property](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
- [Document: createElement() method](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement)
- [EventTarget: addEventListener() method](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
- [Event reference](https://developer.mozilla.org/en-US/docs/Web/Events)
- [<script>: the defer attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script#defer)
- [Webcoding exercises (old Notion page)](https://mctantwerp.notion.site/Exercises-143a7d6a1cfb800ab1d2f7f77ca068c6): The same exercises, with gifs instead of live previews
