# สัปดาห์ที่ 7: DOM & Events (โดเมนและเหตุการณ์)

## 📚 สารบัญ

1. [DOM คืออะไร](#dom-intro)
2. [การเลือก Elements](#selecting-elements)
3. [DOM Navigation](#dom-navigation)
4. [การจัดการ Elements](#manipulating-elements)
5. [Attributes และ Classes](#attributes-classes)
6. [Events (เหตุการณ์)](#events)
7. [Event Listeners](#event-listeners)
8. [Event Delegation](#event-delegation)
9. [ตัวอย่างจริง](#practical-examples)
10. [แบบฝึกหัด](#exercises)

---

## DOM คืออะไร {#dom-intro}

### คำอธิบาย

DOM (Document Object Model) เป็นพื้นฐานของการทำให้ websites interactive ด้วย JavaScript ความเข้าใจ DOM ช่วยให้:

- สามารถเข้าถึง HTML elements ใน page ได้
- แก้ไข content, styling, และ attributes ของ elements
- เพิ่ม หรือ ลบ elements จาก page
- ติดตั้ง event listeners เพื่อตอบสนองต่อการกระทำของผู้ใช้
- สร้าง dynamic และ interactive web applications

DOM คือ "bridge" ระหว่าง HTML/CSS และ JavaScript ที่ทำให้เราสามารถควบคุม page ได้โดยใช้ JavaScript code

### Document Object Model

DOM (Document Object Model) คือ API ที่อนุญาตให้เราเข้าถึง และแก้ไข HTML และ CSS ผ่าน JavaScript

```
Document
├── html
│   ├── head
│   │   ├── meta
│   │   ├── title
│   │   └── link
│   └── body
│       ├── header
│       ├── main
│       │   ├── section
│       │   │   └── h1
│       │   └── div
│       └── footer
```

### DOM Tree Structure

```javascript
// เข้าถึง root element
console.log(document); // Document object
console.log(document.documentElement); // <html> element
console.log(document.body); // <body> element
console.log(document.head); // <head> element
```

---

## การเลือก Elements {#selecting-elements}

### คำอธิบาย

การเลือก (select) elements เป็นขั้นตอนแรกสำคัญในการจัดการ DOM เนื่องจาก:

- ต้องเลือก elements ก่อนจึงจะแก้ไขได้
- วิธีการเลือกต่างๆ มีความเร็วและ flexibility ต่างกัน
- เลือกวิธีที่เหมาะสมสำหรับงานนั้นๆ
- querySelector/querySelectorAll เป็นแนวทางปัจจุบันที่นิยมใช้มากที่สุด

### 1. getElementById() - เลือกด้วย ID

```javascript
// HTML: <div id="header">Header</div>

const element = document.getElementById("header");
console.log(element); // <div id="header">Header</div>

// ได้เพียง element เดียว
const notFound = document.getElementById("nonexistent");
console.log(notFound); // null
```

### 2. getElementsByClassName() - เลือกด้วย Class

```javascript
// HTML:
// <div class="card">Card 1</div>
// <div class="card">Card 2</div>
// <div class="card">Card 3</div>

const elements = document.getElementsByClassName("card");
console.log(elements); // HTMLCollection(3)
console.log(elements[0]); // <div class="card">Card 1</div>
console.log(elements.length); // 3

// Convert to array เพื่อใช้ array methods
const cardsArray = Array.from(elements);
// หรือ
const cardsArray2 = [...elements];
```

### 3. getElementsByTagName() - เลือกด้วย Tag Name

```javascript
// HTML:
// <p>Paragraph 1</p>
// <p>Paragraph 2</p>
// <p>Paragraph 3</p>

const paragraphs = document.getElementsByTagName("p");
console.log(paragraphs); // HTMLCollection(3)
console.log(paragraphs[0]); // <p>Paragraph 1</p>
```

### 4. querySelector() - เลือกตัวแรก (CSS Selector)

```javascript
// CSS selectors เหมือน CSS

// เลือกด้วย ID
const header = document.querySelector("#header");

// เลือกด้วย Class
const card = document.querySelector(".card");

// เลือกด้วย Tag
const firstPara = document.querySelector("p");

// เลือกด้วย Attribute
const link = document.querySelector('[href="https://example.com"]');

// เลือกด้วย Descendant
const cardTitle = document.querySelector(".card h2");

// เลือกด้วย Child
const directChild = document.querySelector("div > p");

// เลือกด้วย Multiple selectors
const heading = document.querySelector("h1, h2, h3");

// ผลลัพธ์ null ถ้าไม่พบ
console.log(document.querySelector(".nonexistent")); // null
```

### 5. querySelectorAll() - เลือกทั้งหมด (CSS Selector)

```javascript
// HTML:
// <div class="card">Card 1</div>
// <div class="card">Card 2</div>
// <div class="card">Card 3</div>

const cards = document.querySelectorAll(".card");
console.log(cards); // NodeList(3)
console.log(cards[0]); // <div class="card">Card 1</div>
console.log(cards.length); // 3

// NodeList สามารถใช้ forEach ได้โดยตรง
cards.forEach((card) => {
  console.log(card);
});

// เลือกแบบซับซ้อน
const activeCards = document.querySelectorAll(".card.active");
const headings = document.querySelectorAll("h1, h2, h3");
const inputs = document.querySelectorAll('input[type="text"]');
```

### เปรียบเทียบวิธีการเลือก

| Method                   | Return         | Speed      | Syntax       |
| ------------------------ | -------------- | ---------- | ------------ |
| getElementById()         | Element        | เร็วที่สุด | ID           |
| getElementsByClassName() | HTMLCollection | เร็ว       | Class        |
| getElementsByTagName()   | HTMLCollection | เร็ว       | Tag          |
| querySelector()          | Element        | ปกติ       | CSS Selector |
| querySelectorAll()       | NodeList       | ปกติ       | CSS Selector |

**Best Practice:** ใช้ `querySelector` และ `querySelectorAll` เพราะ flexible และ modern

---

## DOM Navigation {#dom-navigation}

### คำอธิบาย

DOM Navigation หมายถึงการเคลื่อนที่ผ่านโครงสร้าง DOM tree เพื่อเข้าถึง elements ต่างๆ ความเข้าใจ DOM navigation ช่วยให้:

- จากจุด element หนึ่ง เดินทางไปยัง elements อื่นๆ
- ไม่ต้องเลือก elements ทั้งหมดใหม่ เพียงแค่เดินทางจาก element ที่รู้จักแล้ว
- ทำให้ code มี flexibility มากขึ้น
- สามารถจัดการ elements ที่ generated dynamically ได้ง่ายขึ้น

DOM navigation มีความสำคัญสำหรับการจัดการ complex HTML structures

### Parent, Child, Sibling

```html
<div id="parent">
  <h1>Title</h1>
  <p>Paragraph</p>
  <span>Span text</span>
</div>
```

```javascript
const h1 = document.querySelector("h1");

// Parent node
console.log(h1.parentElement); // <div id="parent">
console.log(h1.parentNode); // <div id="parent"> (ความแตกต่างเล็กน้อย)

// Children
const parent = document.getElementById("parent");
console.log(parent.children); // HTMLCollection(3)
console.log(parent.children[0]); // <h1>Title</h1>
console.log(parent.childNodes); // NodeList (รวม text nodes)
console.log(parent.firstElementChild); // <h1>Title</h1>
console.log(parent.lastElementChild); // <span>Span text</span>

// Siblings
console.log(h1.nextElementSibling); // <p>Paragraph</p>
console.log(h1.previousElementSibling); // null (ไม่มี)

const p = document.querySelector("p");
console.log(p.nextElementSibling); // <span>Span text</span>
console.log(p.previousElementSibling); // <h1>Title</h1>
```

### Traverse ผ่าน DOM

```javascript
// ขึ้นไปหา parent ที่ตรงตามเงื่อนไข
const button = document.querySelector("button");
const card = button.closest(".card"); // ขึ้นไปจนหา .card
console.log(card); // <div class="card">...</div>

// ลงมาหา child ที่ตรงตามเงื่อนไข
const form = document.querySelector("form");
const submitBtn = form.querySelector('[type="submit"]');
console.log(submitBtn); // <button type="submit">Submit</button>
```

---

## การจัดการ Elements {#manipulating-elements}

### คำอธิบาย

การจัดการ (manipulate) elements คือการแก้ไข content, structure, และ styling ของ HTML elements ผ่าน JavaScript ความสามารถนี้ช่วยให้:

- สร้าง dynamic content ที่เปลี่ยนแปลงตามการกระทำของผู้ใช้
- สร้าง Single Page Applications (SPAs) โดยไม่ต้อง reload page
- Update content ของ page โดยไม่กระทบกับ page เอง
- สร้าง user interfaces ที่ interactive
- Manipulate DOM คือ core skill สำหรับ modern web development

### 1. ข้อความและ HTML

#### textContent - ข้อความล้วน

```javascript
// HTML: <div id="box">Hello <strong>World</strong></div>

const box = document.getElementById("box");

// อ่าน
console.log(box.textContent); // 'Hello World'

// เขียน (ลบ HTML tags)
box.textContent = "Goodbye";
// HTML กลายเป็น: <div id="box">Goodbye</div>
```

#### innerHTML - HTML content

```javascript
// HTML: <div id="box">Hello <strong>World</strong></div>

const box = document.getElementById("box");

// อ่าน
console.log(box.innerHTML); // 'Hello <strong>World</strong>'

// เขียน (รักษา HTML tags)
box.innerHTML = "Goodbye <strong>Friend</strong>";
// HTML กลายเป็น: <div id="box">Goodbye <strong>Friend</strong></div>

// ⚠️ ระวัง XSS (Cross-Site Scripting)
// ห้ามใช้ innerHTML กับ user input โดยตรง
const userInput = "<img src=x onerror=\"alert('hacked')\">";
box.innerHTML = userInput; // ⚠️ ปลอดภัยไม่ดี

// แทนที่ใช้ textContent สำหรับ user input
box.textContent = userInput; // ปลอดภัย
```

#### innerText - ข้อความที่มองเห็น

```javascript
// HTML: <div id="box">Hello <span style="display:none">Hidden</span> World</div>

const box = document.getElementById("box");

console.log(box.innerText); // 'Hello  World' (ซ่อน hidden text)
console.log(box.textContent); // 'Hello Hidden World' (รวม hidden text)
```

### 2. การสร้าง Elements

#### createElement()

```javascript
// สร้าง element
const newDiv = document.createElement("div");
newDiv.textContent = "New Box";
newDiv.className = "box-class";
newDiv.id = "new-box";

console.log(newDiv); // <div class="box-class" id="new-box">New Box</div>

// ยังไม่อยู่ใน DOM ต้องเพิ่มเข้าไป
```

#### appendChild() - เพิ่มที่ท้าย

```javascript
const container = document.getElementById("container");
const newDiv = document.createElement("div");
newDiv.textContent = "New Item";

container.appendChild(newDiv);
// <div id="container"><div>New Item</div></div>
```

#### insertBefore() - เพิ่มก่อน element

```javascript
const container = document.getElementById("container");
// HTML: <div id="container"><p>Item 1</p></div>

const newDiv = document.createElement("div");
newDiv.textContent = "Item 0";

const firstChild = container.firstElementChild;
container.insertBefore(newDiv, firstChild);
// HTML: <div id="container"><div>Item 0</div><p>Item 1</p></div>
```

#### insertAdjacentHTML() - เพิ่มที่ตำแหน่งต่างๆ

```javascript
const element = document.querySelector("#box");

// 'beforebegin' - ก่อน element
element.insertAdjacentHTML("beforebegin", "<div>Before</div>");

// 'afterbegin' - หลังเปิด tag (ภายใน ด้านหน้า)
element.insertAdjacentHTML("afterbegin", "<span>Inside Start</span>");

// 'beforeend' - ก่อนปิด tag (ภายใน ด้านท้าย)
element.insertAdjacentHTML("beforeend", "<span>Inside End</span>");

// 'afterend' - หลัง element
element.insertAdjacentHTML("afterend", "<div>After</div>");

// ผลลัพธ์:
// <div>Before</div>
// <div id="box">
//   <span>Inside Start</span>
//   ... (original content)
//   <span>Inside End</span>
// </div>
// <div>After</div>
```

### 3. การลบ Elements

#### removeChild()

```javascript
const container = document.getElementById("container");
const item = container.querySelector(".item");

container.removeChild(item); // ลบ item
```

#### remove()

```javascript
const item = document.querySelector(".item");

item.remove(); // ลบตัวเอง (วิธีง่ายกว่า)
```

#### innerHTML = ''

```javascript
const container = document.getElementById("container");

container.innerHTML = ""; // ลบ content ทั้งหมด
```

### 4. การคัดลอก Elements

```javascript
const original = document.querySelector(".card");

// Shallow clone (ไม่คัดลอก children)
const clone1 = original.cloneNode();

// Deep clone (คัดลอก children)
const clone2 = original.cloneNode(true);

// เพิ่มไปยัง DOM
document.getElementById("container").appendChild(clone2);
```

---

## Attributes และ Classes {#attributes-classes}

### คำอธิบาย

Attributes และ Classes เป็นส่วนสำคัญของการจัดการ DOM เพราะว่า:

- HTML attributes เก็บข้อมูลเพิ่มเติมเกี่ยวกับ elements (href, src, data-\* ฯลฯ)
- Classes ใช้สำหรับการ styling ด้วย CSS และ styling ด้วย JavaScript
- Data attributes (data-\*) ช่วยให้เก็บ custom data เกี่ยวกับ elements
- classList API ทำให้จัดการ classes ได้อย่างสะดวก
- การจัดการ attributes และ classes เป็นวิธีที่ปลอดภัยและ semantic ในการเปลี่ยนแปลง elements

### 1. Attributes

#### getAttribute()

```javascript
// HTML: <a id="link" href="https://example.com" class="external">Link</a>

const link = document.getElementById("link");

console.log(link.getAttribute("href")); // 'https://example.com'
console.log(link.getAttribute("class")); // 'external'
console.log(link.getAttribute("data-id")); // null (ไม่มี)
```

#### setAttribute()

```javascript
const link = document.getElementById("link");

link.setAttribute("href", "https://new-example.com");
link.setAttribute("target", "_blank");
link.setAttribute("data-id", "123");

// HTML กลายเป็น:
// <a id="link" href="https://new-example.com" class="external" target="_blank" data-id="123">Link</a>
```

#### removeAttribute()

```javascript
const link = document.getElementById("link");

link.removeAttribute("target");
// HTML: <a id="link" href="..." class="external">Link</a>
```

#### hasAttribute()

```javascript
const link = document.getElementById("link");

console.log(link.hasAttribute("href")); // true
console.log(link.hasAttribute("target")); // false
```

### 2. Data Attributes

```html
<div id="user" data-id="123" data-name="John" data-role="admin"></div>
```

```javascript
const user = document.getElementById("user");

// อ่าน data attributes
console.log(user.dataset.id); // '123'
console.log(user.dataset.name); // 'John'
console.log(user.dataset.role); // 'admin'

// เขียน data attributes
user.dataset.id = "456";
user.dataset.email = "john@example.com";

// HTML กลายเป็น:
// <div id="user" data-id="456" data-name="John" data-role="admin" data-email="john@example.com"></div>
```

### 3. Class Management

#### classList

```javascript
// HTML: <div id="box" class="container">Box</div>

const box = document.getElementById("box");

// add - เพิ่ม class
box.classList.add("active");
// HTML: <div id="box" class="container active">Box</div>

// remove - ลบ class
box.classList.remove("container");
// HTML: <div id="box" class="active">Box</div>

// toggle - สลับ class (มีก็ลบ ไม่มีก็เพิ่ม)
box.classList.toggle("active");
// HTML: <div id="box">Box</div>

// contains - ตรวจสอบ class มีอยู่หรือไม่
console.log(box.classList.contains("active")); // false

// add หลายตัว
box.classList.add("active", "highlight", "animated");
```

---

## Events (เหตุการณ์) {#events}

### คำอธิบาย

Events คือการกระทำหรือสิ่งที่เกิดขึ้นกับ elements เมื่อผู้ใช้หรือ browser ทำการกระทำบางอย่าง Event system ช่วยให้:

- ตอบสนองต่อการกระทำของผู้ใช้ (คลิก, พิมพ์, ขยับ ฯลฯ)
- สร้าง interactive experiences
- ทำให้ applications เป็น dynamic และ responsive
- จับและประมวลผลสิ่งที่เกิดขึ้นใน page
- Event-driven programming คือ paradigm ที่สำคัญสำหรับ web development

การเข้าใจและใช้งาน events เป็นสิ่งจำเป็นสำหรับการสร้าง interactive web applications

### ประเภท Events ทั่วไป

#### Mouse Events

```javascript
const element = document.querySelector("button");

// click - คลิก
element.addEventListener("click", () => {
  console.log("Clicked!");
});

// dblclick - คลิกสองครั้ง
element.addEventListener("dblclick", () => {
  console.log("Double clicked!");
});

// mouseenter - เมาส์เข้า
element.addEventListener("mouseenter", () => {
  console.log("Mouse entered");
});

// mouseleave - เมาส์ออก
element.addEventListener("mouseleave", () => {
  console.log("Mouse left");
});

// mousemove - เมาส์เคลื่อนไหว
element.addEventListener("mousemove", (event) => {
  console.log("X:", event.clientX, "Y:", event.clientY);
});

// contextmenu - คลิกขวา
element.addEventListener("contextmenu", (event) => {
  event.preventDefault(); // ยกเลิก menu ปกติ
  console.log("Right clicked!");
});
```

#### Keyboard Events

```javascript
const input = document.querySelector("input");

// keydown - กด key
input.addEventListener("keydown", (event) => {
  console.log("Key pressed:", event.key);
});

// keyup - ปล่อย key
input.addEventListener("keyup", (event) => {
  console.log("Key released:", event.key);
});

// keypress - พิมพ์ (deprecated)

// ตรวจสอบ key เฉพาะ
input.addEventListener("keydown", (event) => {
  if (event.key === "Enter") {
    console.log("Enter pressed");
  }
  if (event.key === "Escape") {
    console.log("Escape pressed");
  }

  // modifier keys
  if (event.ctrlKey || event.metaKey) {
    console.log("Ctrl/Cmd pressed");
  }
  if (event.shiftKey) {
    console.log("Shift pressed");
  }
});
```

#### Form Events

```javascript
const input = document.querySelector("input");
const form = document.querySelector("form");

// input - ค่ามีการเปลี่ยน
input.addEventListener("input", (event) => {
  console.log("Input value:", event.target.value);
});

// change - ค่ามีการเปลี่ยน และเสีย focus
input.addEventListener("change", (event) => {
  console.log("Input changed:", event.target.value);
});

// focus - element ได้รับ focus
input.addEventListener("focus", () => {
  console.log("Input focused");
});

// blur - element เสีย focus
input.addEventListener("blur", () => {
  console.log("Input blurred");
});

// submit - form ถูก submit
form.addEventListener("submit", (event) => {
  event.preventDefault(); // ยกเลิก default action
  console.log("Form submitted");
});
```

#### Window Events

```javascript
// load - page โหลดเสร็จ
window.addEventListener("load", () => {
  console.log("Page loaded");
});

// scroll - scroll
window.addEventListener("scroll", () => {
  console.log("Scrolling");
});

// resize - เปลี่ยนขนาด window
window.addEventListener("resize", () => {
  console.log("Window resized");
});
```

---

## Event Listeners {#event-listeners}

### คำอธิบาย

Event Listeners คือวิธีการบอกให้ browser รู้ว่า "เมื่อเกิด event นี้ ให้ทำการ execute function นี้" Event listeners ช่วยให้:

- สามารถ attach behaviors ไปยัง elements ได้
- แยกระหว่าง HTML structure กับ JavaScript logic (separation of concerns)
- สามารถ add, remove, หรือ modify behaviors ได้ง่าย
- จัดการ events ได้หลากหลายวิธี
- addEventListener เป็น modern best practice สำหรับ event handling

### วิธีการเพิ่ม Event Listener

#### 1. addEventListener() - วิธีปกติ (แนะนำ)

```javascript
const button = document.querySelector("button");

button.addEventListener("click", function (event) {
  console.log("Clicked!");
  console.log(event); // Event object
});

// หรือใช้ arrow function
button.addEventListener("click", (event) => {
  console.log("Clicked!");
});
```

#### 2. Inline event handler (ไม่แนะนำ)

```html
<!-- ❌ ไม่ดี -->
<button onclick="console.log('Clicked!')">Click me</button>

<!-- ❌ ไม่ดี -->
<button onclick="handleClick()">Click me</button>

<!-- ✅ ดี -->
<button>Click me</button>

<script>
  const button = document.querySelector("button");
  button.addEventListener("click", handleClick);

  function handleClick() {
    console.log("Clicked!");
  }
</script>
```

### Event Object

```javascript
const button = document.querySelector("button");

button.addEventListener("click", (event) => {
  console.log(event); // Event object
  console.log(event.type); // 'click'
  console.log(event.target); // <button>...</button>
  console.log(event.currentTarget); // <button>...</button>
  console.log(event.timeStamp); // เวลา event
  console.log(event.clientX); // X position (mouse)
  console.log(event.clientY); // Y position (mouse)
});
```

### ลบ Event Listener

```javascript
function handleClick(event) {
  console.log("Clicked!");
}

const button = document.querySelector("button");

// เพิ่ม
button.addEventListener("click", handleClick);

// ลบ (ต้องเป็น function เดียวกัน)
button.removeEventListener("click", handleClick);

// สำหรับ anonymous function ลบได้ยาก
button.addEventListener("click", () => {
  console.log("Clicked!"); // ลบไม่ได้
});
```

### Event Propagation

#### Bubbling - เหตุการณ์ไหลขึ้น

```html
<div id="parent" style="border: 1px solid black; padding: 20px;">
  Parent
  <button id="child">Child Button</button>
</div>

<script>
  const parent = document.getElementById("parent");
  const child = document.getElementById("child");

  parent.addEventListener("click", () => {
    console.log("Parent clicked");
  });

  child.addEventListener("click", (event) => {
    console.log("Child clicked");
    // ผลลัพธ์:
    // Child clicked
    // Parent clicked (bubbling)
  });
</script>
```

#### stopPropagation() - หยุดการ Bubble

```javascript
const parent = document.getElementById("parent");
const child = document.getElementById("child");

parent.addEventListener("click", () => {
  console.log("Parent clicked");
});

child.addEventListener("click", (event) => {
  event.stopPropagation(); // หยุด bubbling
  console.log("Child clicked");
  // ผลลัพธ์:
  // Child clicked (ไม่ bubble ขึ้น)
});
```

#### preventDefault() - ยกเลิก default action

```javascript
const form = document.querySelector("form");
const link = document.querySelector("a");

form.addEventListener("submit", (event) => {
  event.preventDefault(); // ไม่ submit form
  console.log("Form prevented from submitting");
});

link.addEventListener("click", (event) => {
  event.preventDefault(); // ไม่ไป URL
  console.log("Link prevented from navigating");
});
```

---

## Event Delegation {#event-delegation}

### คำอธิบาย

Event Delegation คือ pattern ที่ทำให้ efficient และ flexible เนื่องจาก:

- ไม่ต้อง attach event listeners ไปยังทุก individual elements
- ใช้ event bubbling เพื่อจับ events จาก elements ลูกๆ
- เมื่อ elements ใหม่ถูกเพิ่มเข้า page อย่างไรก็ไม่ต้อง attach listeners ใหม่
- ลดจำนวน event listeners ในหน้า (better performance)
- ทำให้ code เรียบร้อยและ maintainable มากขึ้น

Event delegation เป็น essential pattern สำหรับการจัดการ dynamic lists หรือ collections

### ปัญหา: เพิ่ม listener ให้หลายๆ elements

```javascript
// ❌ ปัญหา: ต้องให้แต่ละ item
const items = document.querySelectorAll(".item");

items.forEach((item) => {
  item.addEventListener("click", () => {
    console.log("Item clicked");
  });
});

// ถ้าเพิ่ม item ใหม่ผ่าน JavaScript ต้องเพิ่ม listener ใหม่อีก
```

### วิธีแก้: Event Delegation

```javascript
// ✅ ดี: ใช้ parent และ event.target
const container = document.querySelector(".container");

container.addEventListener("click", (event) => {
  if (event.target.classList.contains("item")) {
    console.log("Item clicked:", event.target.textContent);
  }
});

// หรือใช้ closest()
container.addEventListener("click", (event) => {
  const item = event.target.closest(".item");
  if (item) {
    console.log("Item clicked:", item.textContent);
  }
});

// ตอนนี้แม้เพิ่ม item ใหม่ก็ยังใช้ได้
const newItem = document.createElement("div");
newItem.className = "item";
newItem.textContent = "New Item";
container.appendChild(newItem); // ✅ ยังคงใช้ได้เลย
```

### ตัวอย่าง To-Do List

```javascript
const todoList = document.querySelector(".todo-list");

todoList.addEventListener("click", (event) => {
  // ลบ item
  if (event.target.classList.contains("delete-btn")) {
    event.target.closest(".todo-item").remove();
  }

  // ทำเครื่องหมายสำเร็จ
  if (event.target.classList.contains("checkbox")) {
    event.target.closest(".todo-item").classList.toggle("completed");
  }
});
```

---

## ตัวอย่างจริง {#practical-examples}

### คำอธิบาย

ตัวอย่างจริงแสดงให้เห็นว่าสิ่งที่เรียนรู้สามารถนำไปใช้กับสถานการณ์จริงอย่างไร ตัวอย่างเหล่านี้:

- ครอบคลุม common scenarios ที่ developers พบบ่อยๆ
- เป็นตัวอย่างของ best practices และ patterns ที่ใช้ได้จริง
- สามารถนำมาปรับเปลี่ยนและใช้กับ projects ของตัวเอง
- แสดงวิธีการ combine DOM manipulation กับ event handling
- เป็นพื้นฐานสำหรับการสร้าง complex features ขั้นสูง

### ตัวอย่าง 1: Counter App

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Counter App</title>
    <style>
      .counter {
        text-align: center;
        padding: 20px;
        border: 1px solid #ccc;
        border-radius: 5px;
      }
      .count {
        font-size: 48px;
        font-weight: bold;
        margin: 20px 0;
      }
      button {
        padding: 10px 20px;
        margin: 0 5px;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div class="counter">
      <h1>Counter</h1>
      <div class="count">0</div>
      <button id="decrease">-</button>
      <button id="reset">Reset</button>
      <button id="increase">+</button>
    </div>

    <script>
      let count = 0;
      const countDisplay = document.querySelector(".count");
      const decreaseBtn = document.getElementById("decrease");
      const resetBtn = document.getElementById("reset");
      const increaseBtn = document.getElementById("increase");

      function updateDisplay() {
        countDisplay.textContent = count;
      }

      decreaseBtn.addEventListener("click", () => {
        count--;
        updateDisplay();
      });

      increaseBtn.addEventListener("click", () => {
        count++;
        updateDisplay();
      });

      resetBtn.addEventListener("click", () => {
        count = 0;
        updateDisplay();
      });
    </script>
  </body>
</html>
```

### ตัวอย่าง 2: Todo List

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Todo List</title>
    <style>
      .todo-list {
        max-width: 500px;
        margin: 20px auto;
      }
      .todo-item {
        display: flex;
        align-items: center;
        padding: 10px;
        border: 1px solid #ddd;
        margin-bottom: 5px;
        border-radius: 3px;
      }
      .todo-item.completed {
        text-decoration: line-through;
        opacity: 0.5;
      }
      .delete-btn {
        margin-left: auto;
        cursor: pointer;
        color: red;
      }
      input[type="text"] {
        flex: 1;
        padding: 10px;
        margin-right: 10px;
      }
    </style>
  </head>
  <body>
    <div class="todo-list">
      <h1>Todo List</h1>
      <div style="margin-bottom: 20px;">
        <input type="text" id="todo-input" placeholder="Add new todo" />
        <button id="add-btn">Add</button>
      </div>
      <div id="todos"></div>
    </div>

    <script>
      const input = document.getElementById("todo-input");
      const addBtn = document.getElementById("add-btn");
      const todosContainer = document.getElementById("todos");

      addBtn.addEventListener("click", addTodo);
      input.addEventListener("keyup", (event) => {
        if (event.key === "Enter") {
          addTodo();
        }
      });

      todosContainer.addEventListener("click", (event) => {
        if (event.target.classList.contains("delete-btn")) {
          event.target.closest(".todo-item").remove();
        }
        if (event.target.classList.contains("checkbox")) {
          event.target.closest(".todo-item").classList.toggle("completed");
        }
      });

      function addTodo() {
        const text = input.value.trim();
        if (!text) return;

        const todoItem = document.createElement("div");
        todoItem.className = "todo-item";
        todoItem.innerHTML = `
        <input type="checkbox" class="checkbox">
        <span>${text}</span>
        <span class="delete-btn">✕</span>
      `;

        todosContainer.appendChild(todoItem);
        input.value = "";
      }
    </script>
  </body>
</html>
```

### ตัวอย่าง 3: Form Validation

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Form Validation</title>
    <style>
      .form-group {
        margin-bottom: 15px;
      }
      label {
        display: block;
        margin-bottom: 5px;
        font-weight: bold;
      }
      input,
      textarea {
        width: 100%;
        padding: 8px;
        border: 1px solid #ccc;
        border-radius: 3px;
      }
      .error {
        color: red;
        font-size: 12px;
        display: none;
      }
      .form-group.invalid input,
      .form-group.invalid textarea {
        border-color: red;
      }
      .form-group.invalid .error {
        display: block;
      }
      button {
        padding: 10px 20px;
        background: blue;
        color: white;
        border: none;
        border-radius: 3px;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <form id="contact-form">
      <div class="form-group">
        <label for="name">Name:</label>
        <input type="text" id="name" required />
        <span class="error">Name is required</span>
      </div>

      <div class="form-group">
        <label for="email">Email:</label>
        <input type="email" id="email" required />
        <span class="error">Valid email is required</span>
      </div>

      <div class="form-group">
        <label for="message">Message:</label>
        <textarea id="message" required></textarea>
        <span class="error">Message is required</span>
      </div>

      <button type="submit">Send</button>
    </form>

    <script>
      const form = document.getElementById("contact-form");
      const nameInput = document.getElementById("name");
      const emailInput = document.getElementById("email");
      const messageInput = document.getElementById("message");

      nameInput.addEventListener("blur", validateName);
      emailInput.addEventListener("blur", validateEmail);
      messageInput.addEventListener("blur", validateMessage);
      form.addEventListener("submit", handleSubmit);

      function validateName() {
        const isValid = nameInput.value.trim().length > 0;
        updateFieldValidity(nameInput, isValid);
        return isValid;
      }

      function validateEmail() {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        const isValid = regex.test(emailInput.value);
        updateFieldValidity(emailInput, isValid);
        return isValid;
      }

      function validateMessage() {
        const isValid = messageInput.value.trim().length > 0;
        updateFieldValidity(messageInput, isValid);
        return isValid;
      }

      function updateFieldValidity(field, isValid) {
        const formGroup = field.closest(".form-group");
        if (isValid) {
          formGroup.classList.remove("invalid");
        } else {
          formGroup.classList.add("invalid");
        }
      }

      function handleSubmit(event) {
        event.preventDefault();

        const isNameValid = validateName();
        const isEmailValid = validateEmail();
        const isMessageValid = validateMessage();

        if (isNameValid && isEmailValid && isMessageValid) {
          alert("Form submitted successfully!");
          form.reset();
        }
      }
    </script>
  </body>
</html>
```

### ตัวอย่าง 4: Image Gallery

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Image Gallery</title>
    <style>
      .gallery {
        max-width: 800px;
        margin: 20px auto;
      }
      .main-image {
        width: 100%;
        height: 400px;
        object-fit: cover;
        border-radius: 5px;
        margin-bottom: 20px;
      }
      .thumbnails {
        display: flex;
        gap: 10px;
        overflow-x: auto;
      }
      .thumbnail {
        width: 80px;
        height: 80px;
        cursor: pointer;
        border: 2px solid transparent;
        border-radius: 3px;
        transition: border-color 0.3s;
      }
      .thumbnail.active {
        border-color: blue;
      }
      .thumbnail:hover {
        opacity: 0.7;
      }
    </style>
  </head>
  <body>
    <div class="gallery">
      <img class="main-image" src="image1.jpg" alt="Main" />
      <div class="thumbnails">
        <img
          class="thumbnail active"
          src="image1.jpg"
          alt="Thumb 1"
          data-full="image1.jpg"
        />
        <img
          class="thumbnail"
          src="image2.jpg"
          alt="Thumb 2"
          data-full="image2.jpg"
        />
        <img
          class="thumbnail"
          src="image3.jpg"
          alt="Thumb 3"
          data-full="image3.jpg"
        />
        <img
          class="thumbnail"
          src="image4.jpg"
          alt="Thumb 4"
          data-full="image4.jpg"
        />
      </div>
    </div>

    <script>
      const mainImage = document.querySelector(".main-image");
      const thumbnails = document.querySelectorAll(".thumbnail");

      thumbnails.forEach((thumbnail) => {
        thumbnail.addEventListener("click", () => {
          // ลบ active class จากทั้งหมด
          thumbnails.forEach((t) => t.classList.remove("active"));

          // เพิ่ม active class ให้ clicked thumbnail
          thumbnail.classList.add("active");

          // เปลี่ยน main image
          mainImage.src = thumbnail.dataset.full;
        });
      });
    </script>
  </body>
</html>
```

---

## แบบฝึกหัด {#exercises}

### คำอธิบาย

แบบฝึกหัดเหล่านี้ออกแบบมาเพื่อให้:

- ฝึกปฏิบัติ DOM manipulation และ event handling
- เพิ่มความเข้าใจผ่าน hands-on projects
- สร้างความมั่นใจในการสร้าง interactive features
- ก้าวจาก simple tasks ไปยัง complex projects
- ได้ประสบการณ์กับ real-world scenarios

แบบฝึกหัดเรียงจาก Easy (ง่าย) ไป Hard (ยาก) เพื่อให้ความท้าทายเพิ่มขึ้นทีละน้อย

### ระดับ Easy

#### 1. Color Changer

```javascript
// สร้างปุ่ม 3 ตัว (Red, Green, Blue)
// เมื่อคลิกปุ่ม ให้เปลี่ยนสีพื้นหลังของ page
```

#### 2. Show/Hide Element

```javascript
// สร้างปุ่ม Toggle
// เมื่อคลิก ให้ซ่อน/แสดง div
```

#### 3. Text Input Display

```javascript
// เมื่อพิมพ์ในช่อง input ให้แสดงข้อความใน div
```

### ระดับ Medium

#### 4. Interactive List

```javascript
// สร้าง input และปุ่ม Add
// เมื่อคลิก Add ให้เพิ่ม item เข้า list
// สามารถลบ item ได้ด้วย
// สามารถทำเครื่องหมาย item ได้
```

#### 5. Accordion Menu

```javascript
// สร้าง accordion ที่มี 3 sections
// เมื่อคลิก section ให้ขยาย/ยุบ
// เฉพาะ 1 section เท่านั้นที่ขยายได้
```

#### 6. Modal Dialog

```javascript
// สร้างปุ่มเปิด Modal
// Modal มีปุ่มปิด
// ลบ modal เมื่อคลิก ปุ่มปิดหรือพื้นหลัง
```

### ระดับ Hard

#### 7. Real-time Search

```javascript
// สร้าง input ค้นหา
// มี list ของข้อมูล (ชื่อผู้คน)
// เมื่อพิมพ์ ให้กรอง list แบบ real-time
// ไม่ case-sensitive
```

#### 8. Drag and Drop

```javascript
// สร้าง draggable elements
// สร้าง drop zones
// ลาก elements ไปยัง drop zones
// บันทึกตำแหน่งที่ลาก
```

#### 9. Shopping Cart

```javascript
// สร้างหน้าจำหน่ายสินค้า (ชื่อ, ราคา, รูป)
// ปุ่ม Add to Cart
// แสดง cart ด้านข้าง
// คำนวณราคารวม
// สามารถเปลี่ยน quantity และลบสินค้า
```

#### 10. Advanced Form Validation

```javascript
// สร้าง form สมัครสมาชิก
// ตรวจสอบ:
// - Username (3-20 ตัวอักษร)
// - Email (valid format)
// - Password (min 8 ตัวอักษร, ต้องมีตัวเลข)
// - Confirm Password
// - Terms & Conditions (ต้อง check)
// แสดง error messages
// Disable submit button จนกว่า valid
```

---

## สรุป

- **DOM** คือ JavaScript API ที่ใช้จัดการ HTML
- **querySelector/querySelectorAll** ทำให้ง่ายและ flexible ที่สุด
- **textContent** สำหรับข้อความ, **innerHTML** สำหรับ HTML (ระวัง XSS)
- **createElement + appendChild** เพื่อสร้าง elements
- **classList** เพื่อจัดการ classes
- **addEventListener** สำหรับจับเหตุการณ์
- **Event Delegation** ทำให้ efficient สำหรับหลายๆ elements
- **preventDefault()** เพื่อยกเลิก default actions
- **event.target** เพื่อรู้ว่า element ไหนถูก trigger

---

## แหล่งอ้างอิง

- [MDN - DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
- [MDN - DOM API](https://developer.mozilla.org/en-US/docs/Web/API/DOM)
- [MDN - Events](https://developer.mozilla.org/en-US/docs/Web/Events)
- [JavaScript.info - DOM](https://javascript.info/dom-nodes)
- [JavaScript.info - Events](https://javascript.info/events)
