You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/th/3-terrarium/3-intro-to-DOM-and-closures/README.md

445 lines
47 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "bc93f6285423033ebf5b8abeb5282888",
"translation_date": "2025-10-23T21:02:43+00:00",
"source_file": "3-terrarium/3-intro-to-DOM-and-closures/README.md",
"language_code": "th"
}
-->
# โครงการ Terrarium ตอนที่ 3: การจัดการ DOM และ JavaScript Closures
![DOM และ closure](../../../../translated_images/webdev101-js.10280393044d7eaaec7e847574946add7ddae6be2b2194567d848b61d849334a.th.png)
> ภาพวาดโดย [Tomomi Imura](https://twitter.com/girlie_mac)
ยินดีต้อนรับสู่หนึ่งในแง่มุมที่น่าสนใจที่สุดของการพัฒนาเว็บ - การทำให้สิ่งต่าง ๆ มีความโต้ตอบ! Document Object Model (DOM) เป็นเหมือนสะพานเชื่อมระหว่าง HTML และ JavaScript ของคุณ และวันนี้เราจะใช้มันเพื่อทำให้ terrarium ของคุณมีชีวิตชีวา เมื่อ Tim Berners-Lee สร้างเว็บเบราว์เซอร์ตัวแรก เขาได้จินตนาการถึงเว็บที่เอกสารสามารถเป็นแบบไดนามิกและโต้ตอบได้ - DOM ทำให้วิสัยทัศน์นั้นเป็นไปได้
เราจะสำรวจ JavaScript closures ซึ่งอาจฟังดูน่ากลัวในตอนแรก ลองนึกถึง closures ว่าเป็นการสร้าง "กระเป๋าความจำ" ที่ฟังก์ชันของคุณสามารถจดจำข้อมูลสำคัญได้ มันเหมือนกับแต่ละต้นไม้ใน terrarium ของคุณมีบันทึกข้อมูลของตัวเองเพื่อติดตามตำแหน่งของมัน เมื่อจบบทเรียนนี้ คุณจะเข้าใจว่ามันเป็นธรรมชาติและมีประโยชน์อย่างไร
นี่คือสิ่งที่เรากำลังสร้าง: terrarium ที่ผู้ใช้สามารถลากและวางต้นไม้ได้ทุกที่ที่ต้องการ คุณจะได้เรียนรู้เทคนิคการจัดการ DOM ที่ช่วยให้ทุกอย่างตั้งแต่การอัปโหลดไฟล์แบบลากและวางไปจนถึงเกมแบบโต้ตอบ มาทำให้ terrarium ของคุณมีชีวิตกันเถอะ
## แบบทดสอบก่อนเรียน
[แบบทดสอบก่อนเรียน](https://ff-quizzes.netlify.app/web/quiz/19)
## ทำความเข้าใจ DOM: ประตูสู่หน้าเว็บแบบโต้ตอบ
Document Object Model (DOM) คือวิธีที่ JavaScript สื่อสารกับองค์ประกอบ HTML ของคุณ เมื่อเบราว์เซอร์ของคุณโหลดหน้า HTML มันจะสร้างการแสดงผลแบบโครงสร้างของหน้านั้นในหน่วยความจำ - นั่นคือ DOM ลองนึกถึงมันว่าเป็นแผนผังครอบครัวที่ทุกองค์ประกอบ HTML เป็นสมาชิกครอบครัวที่ JavaScript สามารถเข้าถึง ปรับเปลี่ยน หรือจัดเรียงใหม่ได้
การจัดการ DOM เปลี่ยนหน้าคงที่ให้เป็นเว็บไซต์แบบโต้ตอบ ทุกครั้งที่คุณเห็นปุ่มเปลี่ยนสีเมื่อเลื่อนเมาส์ เนื้อหาที่อัปเดตโดยไม่ต้องรีเฟรชหน้า หรือองค์ประกอบที่คุณสามารถลากไปรอบ ๆ ได้ นั่นคือการจัดการ DOM ที่ทำงานอยู่
![การแสดงผลแผนผัง DOM](../../../../translated_images/dom-tree.7daf0e763cbbba9273f9a66fe04c98276d7d23932309b195cb273a9cf1819b42.th.png)
> การแสดงผล DOM และ HTML markup ที่อ้างอิงถึงมัน จาก [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
**สิ่งที่ทำให้ DOM ทรงพลัง:**
- **ให้** วิธีการที่มีโครงสร้างในการเข้าถึงองค์ประกอบใด ๆ บนหน้าเว็บของคุณ
- **ช่วยให้** อัปเดตเนื้อหาแบบไดนามิกโดยไม่ต้องรีเฟรชหน้า
- **อนุญาต** การตอบสนองแบบเรียลไทม์ต่อการโต้ตอบของผู้ใช้ เช่น การคลิกและการลาก
- **สร้าง** พื้นฐานสำหรับแอปพลิเคชันเว็บแบบโต้ตอบสมัยใหม่
## JavaScript Closures: สร้างโค้ดที่มีการจัดระเบียบและทรงพลัง
[JavaScript closure](https://developer.mozilla.org/docs/Web/JavaScript/Closures) เปรียบเสมือนการให้ฟังก์ชันมีพื้นที่ทำงานส่วนตัวพร้อมหน่วยความจำที่คงอยู่ ลองนึกถึงนกฟินช์ของดาร์วินบนหมู่เกาะกาลาปาโกสที่แต่ละตัวพัฒนาปากที่เชี่ยวชาญตามสภาพแวดล้อมเฉพาะของมัน - closures ทำงานในลักษณะเดียวกัน สร้างฟังก์ชันเฉพาะที่ "จดจำ" บริบทเฉพาะของมันแม้ว่าฟังก์ชันแม่จะเสร็จสิ้นแล้วก็ตาม
ใน terrarium ของเรา closures ช่วยให้แต่ละต้นไม้จดจำตำแหน่งของตัวเองได้อย่างอิสระ รูปแบบนี้ปรากฏอยู่ทั่วการพัฒนา JavaScript ระดับมืออาชีพ ทำให้เป็นแนวคิดที่มีคุณค่าในการทำความเข้าใจ
> 💡 **ทำความเข้าใจ Closures**: Closures เป็นหัวข้อสำคัญใน JavaScript และนักพัฒนาหลายคนใช้มันมาหลายปีก่อนที่จะเข้าใจทุกแง่มุมทางทฤษฎี วันนี้เรามุ่งเน้นที่การใช้งานจริง - คุณจะเห็น closures เกิดขึ้นตามธรรมชาติเมื่อเราสร้างคุณสมบัติแบบโต้ตอบ ความเข้าใจจะพัฒนาขึ้นเมื่อคุณเห็นว่ามันแก้ปัญหาจริงได้อย่างไร
![การแสดงผลแผนผัง DOM](../../../../translated_images/dom-tree.7daf0e763cbbba9273f9a66fe04c98276d7d23932309b195cb273a9cf1819b42.th.png)
> การแสดงผล DOM และ HTML markup ที่อ้างอิงถึงมัน จาก [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
ในบทเรียนนี้ เราจะทำโครงการ terrarium แบบโต้ตอบของเราให้เสร็จสมบูรณ์โดยการสร้าง JavaScript ที่จะช่วยให้ผู้ใช้สามารถจัดการต้นไม้บนหน้าเว็บได้
## ก่อนเริ่มต้น: การเตรียมตัวให้พร้อม
คุณจะต้องใช้ไฟล์ HTML และ CSS จากบทเรียน terrarium ก่อนหน้านี้ - เรากำลังจะทำให้การออกแบบแบบคงที่นั้นมีความโต้ตอบ หากคุณเพิ่งเข้าร่วมครั้งแรก การทำบทเรียนเหล่านั้นให้เสร็จก่อนจะช่วยให้คุณเข้าใจบริบทที่สำคัญ
นี่คือสิ่งที่เราจะสร้าง:
- **การลากและวางที่ราบรื่น** สำหรับต้นไม้ใน terrarium ทั้งหมด
- **การติดตามพิกัด** เพื่อให้ต้นไม้จดจำตำแหน่งของมัน
- **อินเทอร์เฟซแบบโต้ตอบที่สมบูรณ์** โดยใช้ JavaScript แบบ vanilla
- **โค้ดที่สะอาดและมีการจัดระเบียบ** โดยใช้รูปแบบ closure
## การตั้งค่าไฟล์ JavaScript ของคุณ
มาสร้างไฟล์ JavaScript ที่จะทำให้ terrarium ของคุณมีความโต้ตอบกันเถอะ
**ขั้นตอนที่ 1: สร้างไฟล์ script ของคุณ**
ในโฟลเดอร์ terrarium ของคุณ สร้างไฟล์ใหม่ชื่อ `script.js`
**ขั้นตอนที่ 2: เชื่อมโยง JavaScript กับ HTML ของคุณ**
เพิ่มแท็ก script นี้ในส่วน `<head>` ของไฟล์ `index.html` ของคุณ:
```html
<script src="./script.js" defer></script>
```
**ทำไมแอตทริบิวต์ `defer` ถึงสำคัญ:**
- **รับรอง** ว่า JavaScript ของคุณรอจนกว่า HTML ทั้งหมดจะโหลดเสร็จ
- **ป้องกัน** ข้อผิดพลาดที่ JavaScript พยายามค้นหาองค์ประกอบที่ยังไม่พร้อม
- **รับประกัน** ว่าองค์ประกอบต้นไม้ทั้งหมดของคุณพร้อมสำหรับการโต้ตอบ
- **ให้** ประสิทธิภาพที่ดีกว่าการวาง script ไว้ที่ด้านล่างของหน้า
> ⚠️ **หมายเหตุสำคัญ**: แอตทริบิวต์ `defer` ป้องกันปัญหาเรื่องเวลาโดยทั่วไป หากไม่มีมัน JavaScript อาจพยายามเข้าถึงองค์ประกอบ HTML ก่อนที่มันจะโหลดเสร็จ ซึ่งจะทำให้เกิดข้อผิดพลาด
---
## การเชื่อมโยง JavaScript กับองค์ประกอบ HTML ของคุณ
ก่อนที่เราจะทำให้องค์ประกอบสามารถลากได้ JavaScript จำเป็นต้องค้นหาองค์ประกอบเหล่านั้นใน DOM ลองนึกถึงสิ่งนี้เหมือนระบบการจัดหมวดหมู่ในห้องสมุด - เมื่อคุณมีหมายเลขหมวดหมู่ คุณสามารถค้นหาหนังสือที่คุณต้องการและเข้าถึงเนื้อหาทั้งหมดของมันได้
เราจะใช้เมธอด `document.getElementById()` เพื่อทำการเชื่อมโยงเหล่านี้ มันเหมือนกับการมีระบบการจัดเก็บที่แม่นยำ - คุณให้ ID และมันจะค้นหาองค์ประกอบที่คุณต้องการใน HTML ของคุณ
### การเปิดใช้งานฟังก์ชันการลากสำหรับต้นไม้ทั้งหมด
เพิ่มโค้ดนี้ในไฟล์ `script.js` ของคุณ:
```javascript
// Enable drag functionality for all 14 plants
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
dragElement(document.getElementById('plant4'));
dragElement(document.getElementById('plant5'));
dragElement(document.getElementById('plant6'));
dragElement(document.getElementById('plant7'));
dragElement(document.getElementById('plant8'));
dragElement(document.getElementById('plant9'));
dragElement(document.getElementById('plant10'));
dragElement(document.getElementById('plant11'));
dragElement(document.getElementById('plant12'));
dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));
```
**นี่คือสิ่งที่โค้ดนี้ทำ:**
- **ค้นหา** องค์ประกอบต้นไม้แต่ละตัวใน DOM โดยใช้ ID เฉพาะของมัน
- **ดึง** การอ้างอิง JavaScript ไปยังองค์ประกอบ HTML แต่ละตัว
- **ส่งต่อ** องค์ประกอบแต่ละตัวไปยังฟังก์ชัน `dragElement` (ซึ่งเราจะสร้างต่อไป)
- **เตรียม** ต้นไม้ทุกต้นสำหรับการโต้ตอบแบบลากและวาง
- **เชื่อมโยง** โครงสร้าง HTML ของคุณกับฟังก์ชัน JavaScript
> 🎯 **ทำไมต้องใช้ IDs แทน Classes?** IDs ให้ตัวระบุเฉพาะสำหรับองค์ประกอบเฉพาะ ในขณะที่ CSS classes ถูกออกแบบมาเพื่อการจัดสไตล์กลุ่มขององค์ประกอบ เมื่อ JavaScript ต้องการจัดการองค์ประกอบแต่ละตัว IDs ให้ความแม่นยำและประสิทธิภาพที่เราต้องการ
> 💡 **เคล็ดลับ**: สังเกตว่าเรากำลังเรียก `dragElement()` สำหรับต้นไม้แต่ละต้นแยกกัน วิธีนี้ช่วยให้ต้นไม้แต่ละต้นมีพฤติกรรมการลากที่เป็นอิสระ ซึ่งจำเป็นสำหรับการโต้ตอบของผู้ใช้ที่ราบรื่น
---
## การสร้าง Drag Element Closure
ตอนนี้เราจะสร้างหัวใจของฟังก์ชันการลากของเรา: closure ที่จัดการพฤติกรรมการลากสำหรับต้นไม้แต่ละต้น Closure นี้จะมีฟังก์ชันภายในหลายตัวที่ทำงานร่วมกันเพื่อติดตามการเคลื่อนไหวของเมาส์และอัปเดตตำแหน่งขององค์ประกอบ
Closures เหมาะสำหรับงานนี้เพราะช่วยให้เราสร้างตัวแปร "ส่วนตัว" ที่คงอยู่ระหว่างการเรียกฟังก์ชัน ทำให้ต้นไม้แต่ละต้นมีระบบการติดตามพิกัดที่เป็นอิสระ
### ทำความเข้าใจ Closures ด้วยตัวอย่างง่าย ๆ
ให้ฉันแสดงตัวอย่างง่าย ๆ ที่แสดงแนวคิดของ closures:
```javascript
function createCounter() {
let count = 0; // This is like a private variable
function increment() {
count++; // The inner function remembers the outer variable
return count;
}
return increment; // We're giving back the inner function
}
const myCounter = createCounter();
console.log(myCounter()); // 1
console.log(myCounter()); // 2
```
**นี่คือสิ่งที่เกิดขึ้นในรูปแบบ closure นี้:**
- **สร้าง** ตัวแปร `count` ส่วนตัวที่มีอยู่เฉพาะใน closure นี้
- **ฟังก์ชันภายใน** สามารถเข้าถึงและแก้ไขตัวแปรภายนอกนั้น (กลไกของ closure)
- **เมื่อเราส่งคืน** ฟังก์ชันภายใน มันยังคงเชื่อมต่อกับข้อมูลส่วนตัวนั้น
- **แม้หลังจาก** `createCounter()` เสร็จสิ้นการทำงาน `count` ยังคงอยู่และจดจำค่าของมัน
### ทำไม Closures ถึงเหมาะสำหรับฟังก์ชันการลาก
สำหรับ terrarium ของเรา ต้นไม้แต่ละต้นจำเป็นต้องจดจำพิกัดตำแหน่งปัจจุบันของมัน Closures ให้โซลูชันที่สมบูรณ์แบบ:
**ประโยชน์สำคัญสำหรับโครงการของเรา:**
- **รักษา** ตัวแปรตำแหน่งส่วนตัวสำหรับต้นไม้แต่ละต้นอย่างอิสระ
- **คงไว้** ข้อมูลพิกัดระหว่างเหตุการณ์การลาก
- **ป้องกัน** ความขัดแย้งของตัวแปรระหว่างองค์ประกอบที่ลากได้ต่าง ๆ
- **สร้าง** โครงสร้างโค้ดที่สะอาดและมีการจัดระเบียบ
> 🎯 **เป้าหมายการเรียนรู้**: คุณไม่จำเป็นต้องเข้าใจทุกแง่มุมของ closures ในตอนนี้ มุ่งเน้นที่การเห็นว่ามันช่วยเราในการจัดระเบียบโค้ดและรักษาสถานะสำหรับฟังก์ชันการลากอย่างไร
### การสร้างฟังก์ชัน dragElement
ตอนนี้มาสร้างฟังก์ชันหลักที่จะจัดการตรรกะการลากทั้งหมด เพิ่มฟังก์ชันนี้ด้านล่างการประกาศองค์ประกอบต้นไม้ของคุณ:
```javascript
function dragElement(terrariumElement) {
// Initialize position tracking variables
let pos1 = 0, // Previous mouse X position
pos2 = 0, // Previous mouse Y position
pos3 = 0, // Current mouse X position
pos4 = 0; // Current mouse Y position
// Set up the initial drag event listener
terrariumElement.onpointerdown = pointerDrag;
}
```
**ทำความเข้าใจระบบการติดตามตำแหน่ง:**
- **`pos1` และ `pos2`**: เก็บความแตกต่างระหว่างตำแหน่งเมาส์เก่าและใหม่
- **`pos3` และ `pos4`**: ติดตามพิกัดเมาส์ปัจจุบัน
- **`terrariumElement`**: องค์ประกอบต้นไม้เฉพาะที่เรากำลังทำให้ลากได้
- **`onpointerdown`**: เหตุการณ์ที่เริ่มต้นเมื่อผู้ใช้เริ่มลาก
**นี่คือวิธีการทำงานของรูปแบบ closure:**
- **สร้าง** ตัวแปรตำแหน่งส่วนตัวสำหรับองค์ประกอบต้นไม้แต่ละตัว
- **คงไว้** ตัวแปรเหล่านี้ตลอดวงจรการลาก
- **รับรอง** ว่าต้นไม้แต่ละต้นติดตามพิกัดของตัวเองอย่างอิสระ
- **ให้** อินเทอร์เฟซที่สะอาดผ่านฟังก์ชัน `dragElement`
### ทำไมต้องใช้ Pointer Events?
คุณอาจสงสัยว่าทำไมเราถึงใช้ `onpointerdown` แทน `onclick` ที่คุ้นเคย นี่คือเหตุผล:
| ประเภทเหตุการณ์ | เหมาะสำหรับ | ข้อจำกัด |
|------------------|-------------|-----------|
| `onclick` | การคลิกปุ่มง่าย ๆ | ไม่สามารถจัดการการลากได้ (แค่คลิกและปล่อย) |
| `onpointerdown` | ทั้งเมาส์และสัมผัส | ใหม่กว่า แต่รองรับได้ดีในปัจจุบัน |
| `onmousedown` | เฉพาะเมาส์เดสก์ท็อป | ไม่รองรับผู้ใช้มือถือ |
**ทำไม pointer events ถึงเหมาะสำหรับสิ่งที่เรากำลังสร้าง:**
- **ทำงานได้ดี** ไม่ว่าคนจะใช้เมาส์ นิ้ว หรือแม้กระทั่งปากกา
- **ให้ความรู้สึกเหมือนกัน** บนแล็ปท็อป แท็บเล็ต หรือโทรศัพท์
- **จัดการ** การเคลื่อนไหวการลากจริง (ไม่ใช่แค่คลิกและเสร็จสิ้น)
- **สร้าง** ประสบการณ์ที่ราบรื่นที่ผู้ใช้คาดหวังจากแอปเว็บสมัยใหม่
> 💡 **การเตรียมตัวสำหรับอนาคต**: Pointer events เป็นวิธีการจัดการการโต้ตอบของผู้ใช้ในยุคใหม่ แทนที่จะเขียนโค้ดแยกสำหรับเมาส์และสัมผัส คุณจะได้ทั้งสองอย่างฟรี น่าสนใจใช่ไหม?
---
## ฟังก์ชัน pointerDrag: การจับการเริ่มต้นการลาก
เมื่อผู้ใช้กดลงบนต้นไม้ (ไม่ว่าจะด้วยการคลิกเมาส์หรือสัมผัสนิ้ว) ฟังก์ชัน `pointerDrag` จะเริ่มทำงาน ฟังก์ชันนี้จับพิกัดเริ่มต้นและตั้งค่าระบบการลาก
เพิ่มฟังก์ชันนี้ภายใน closure `dragElement` หลังบรรทัด `terrariumElement.onpointerdown = pointerDrag;`:
```javascript
function pointerDrag(e) {
// Prevent default browser behavior (like text selection)
e.preventDefault();
// Capture the initial mouse/touch position
pos3 = e.clientX; // X coordinate where drag started
pos4 = e.clientY; // Y coordinate where drag started
// Set up event listeners for the dragging process
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
}
```
**ทีละขั้นตอน นี่คือสิ่งที่เกิดขึ้น:**
- **ป้องกัน** พฤติกรรมเริ่มต้นของเบราว์เซอร์ที่อาจรบกวนการลาก
- **บันทึก** พิกัดที่แน่นอนที่ผู้ใช้เริ่มต้นการลาก
- **ตั้งค่า** event listeners สำหรับการเคลื่อนไหวการลากที่กำลังดำเนินอยู่
- **เตรียม** ระบบเพื่อติดตามการเคลื่อนไหวเมาส์/นิ้วทั่วทั้งเอกสาร
### ทำความเข้าใจการป้องกันเหตุการณ์
บรรทัด `e.preventDefault()` มีความสำคัญต่อการลากที่ราบรื่น:
**หากไม่มีการป้องกัน เบราว์เซอร์อาจ:**
- **เลือก** ข้อความเมื่อลากผ่านหน้าเว็บ
- **เรียกใช้** เมนูบริบทเมื่อคลิกขวาและลาก
- **รบกวน** พฤติกรรมการลากที่กำหนดเองของเรา
- **สร้าง** สิ่งประดิษฐ์ภาพระหว่างการลาก
> 🔍 **ทดลอง**: หลังจากจบบทเรียนนี้ ลองลบบรรทัด `e.preventDefault()` และดูว่ามันส่งผลต่อประสบการณ์การลากอย่างไร คุณจะเข้าใจได้อย่างรวดเร็วว่าทำไมบรรทัดนี้ถึงจำเป็น!
### ระบบการติดตามพิกัด
คุณสมบัติ `e.clientX` และ `e.clientY` ให้พิกัดเมาส์/สัมผัสที่แม่นยำ:
| คุณสมบัติ | สิ่งที่วัด | กรณีการใช้งาน |
|-----------|------------|----------------|
| `clientX` | ตำแหน่งแนวนอนสัมพันธ์กับ viewport | ติดตามการเคลื่อนไหวซ้าย-ขวา |
| `clientY` | ตำแหน่งแนวตั้งสัมพันธ์กับ viewport | ติดตามการเคลื่อนไหวขึ้น-ลง |
**ทำความเข้าใจพิกัดเหล่านี้:**
- **ให้** ข้อมูลตำ
- **`pos3` และ `pos4`**: เก็บตำแหน่งเมาส์ปัจจุบันเพื่อใช้ในการคำนวณครั้งถัดไป
- **`offsetTop` และ `offsetLeft`**: รับตำแหน่งปัจจุบันขององค์ประกอบบนหน้าเว็บ
- **ตรรกะการลบ**: ย้ายองค์ประกอบตามระยะที่เมาส์เคลื่อนที่
**นี่คือการคำนวณการเคลื่อนไหวแบบละเอียด:**
1. **วัด** ความแตกต่างระหว่างตำแหน่งเมาส์เก่าและใหม่
2. **คำนวณ** ระยะที่ต้องย้ายองค์ประกอบตามการเคลื่อนที่ของเมาส์
3. **อัปเดต** คุณสมบัติตำแหน่ง CSS ขององค์ประกอบแบบเรียลไทม์
4. **เก็บ** ตำแหน่งใหม่เป็นฐานสำหรับการคำนวณการเคลื่อนไหวครั้งถัดไป
### การแสดงภาพของการคำนวณทางคณิตศาสตร์
```mermaid
sequenceDiagram
participant Mouse
participant JavaScript
participant Plant
Mouse->>JavaScript: Move from (100,50) to (110,60)
JavaScript->>JavaScript: Calculate: moved 10px right, 10px down
JavaScript->>Plant: Update position by +10px right, +10px down
Plant->>Plant: Render at new position
```
### ฟังก์ชัน stopElementDrag: การทำความสะอาด
เพิ่มฟังก์ชันการทำความสะอาดหลังปีกกาปิดของ `elementDrag`:
```javascript
function stopElementDrag() {
// Remove the document-level event listeners
document.onpointerup = null;
document.onpointermove = null;
}
```
**ทำไมการทำความสะอาดถึงสำคัญ:**
- **ป้องกัน** การรั่วไหลของหน่วยความจำจากตัวฟังเหตุการณ์ที่ยังค้างอยู่
- **หยุด** พฤติกรรมการลากเมื่อผู้ใช้ปล่อยต้นไม้
- **อนุญาต** ให้องค์ประกอบอื่นสามารถลากได้อย่างอิสระ
- **รีเซ็ต** ระบบสำหรับการดำเนินการลากครั้งถัดไป
**สิ่งที่เกิดขึ้นหากไม่มีการทำความสะอาด:**
- ตัวฟังเหตุการณ์ยังคงทำงานแม้หลังจากการลากหยุดลง
- ประสิทธิภาพลดลงเนื่องจากตัวฟังที่ไม่ได้ใช้งานสะสม
- พฤติกรรมที่ไม่คาดคิดเมื่อโต้ตอบกับองค์ประกอบอื่น
- ทรัพยากรของเบราว์เซอร์ถูกใช้ไปกับการจัดการเหตุการณ์ที่ไม่จำเป็น
### ทำความเข้าใจคุณสมบัติตำแหน่ง CSS
ระบบการลากของเราจัดการกับคุณสมบัติ CSS หลักสองตัว:
| คุณสมบัติ | สิ่งที่ควบคุม | วิธีการใช้งาน |
|-----------|----------------|----------------|
| `top` | ระยะห่างจากขอบด้านบน | การจัดตำแหน่งแนวตั้งระหว่างการลาก |
| `left` | ระยะห่างจากขอบด้านซ้าย | การจัดตำแหน่งแนวนอนระหว่างการลาก |
**ข้อมูลสำคัญเกี่ยวกับคุณสมบัติ offset:**
- **`offsetTop`**: ระยะห่างปัจจุบันจากด้านบนขององค์ประกอบแม่ที่มีการกำหนดตำแหน่ง
- **`offsetLeft`**: ระยะห่างปัจจุบันจากด้านซ้ายขององค์ประกอบแม่ที่มีการกำหนดตำแหน่ง
- **บริบทการกำหนดตำแหน่ง**: ค่าพวกนี้สัมพันธ์กับบรรพบุรุษที่มีการกำหนดตำแหน่งใกล้ที่สุด
- **การอัปเดตแบบเรียลไทม์**: เปลี่ยนแปลงทันทีเมื่อเราปรับคุณสมบัติ CSS
> 🎯 **ปรัชญาการออกแบบ**: ระบบการลากนี้มีความยืดหยุ่นโดยตั้งใจ ไม่มี "โซนวาง" หรือข้อจำกัดใดๆ ผู้ใช้สามารถวางต้นไม้ได้ทุกที่ ทำให้พวกเขามีอิสระในการออกแบบสวนขวดของตัวเองอย่างเต็มที่
## รวมทุกอย่างเข้าด้วยกัน: ระบบลากของคุณที่สมบูรณ์
ยินดีด้วย! คุณเพิ่งสร้างระบบลากและวางที่ซับซ้อนโดยใช้ JavaScript แบบพื้นฐาน ฟังก์ชัน `dragElement` ของคุณตอนนี้มี closure ที่ทรงพลังซึ่งจัดการ:
**สิ่งที่ closure ของคุณทำได้:**
- **รักษา** ตัวแปรตำแหน่งส่วนตัวสำหรับต้นไม้แต่ละต้นอย่างอิสระ
- **จัดการ** วงจรการลากทั้งหมดตั้งแต่ต้นจนจบ
- **ให้** การเคลื่อนไหวที่ราบรื่นและตอบสนองทั่วทั้งหน้าจอ
- **ทำความสะอาด** ทรัพยากรอย่างเหมาะสมเพื่อป้องกันการรั่วไหลของหน่วยความจำ
- **สร้าง** อินเทอร์เฟซที่ใช้งานง่ายและสร้างสรรค์สำหรับการออกแบบสวนขวด
### ทดสอบสวนขวดแบบโต้ตอบของคุณ
ตอนนี้ทดสอบสวนขวดแบบโต้ตอบของคุณ! เปิดไฟล์ `index.html` ในเว็บเบราว์เซอร์และลองใช้งาน:
1. **คลิกและกดค้าง** ที่ต้นไม้ใดๆ เพื่อเริ่มการลาก
2. **เคลื่อนเมาส์หรือใช้นิ้ว** และดูต้นไม้เคลื่อนที่ตามอย่างราบรื่น
3. **ปล่อย** เพื่อวางต้นไม้ในตำแหน่งใหม่
4. **ทดลอง** จัดเรียงในรูปแบบต่างๆ เพื่อสำรวจอินเทอร์เฟซ
🥇 **ความสำเร็จ**: คุณได้สร้างแอปพลิเคชันเว็บแบบโต้ตอบเต็มรูปแบบโดยใช้แนวคิดหลักที่นักพัฒนามืออาชีพใช้ในชีวิตประจำวัน ฟังก์ชันการลากและวางนี้ใช้หลักการเดียวกันกับการอัปโหลดไฟล์ กระดานงาน และอินเทอร์เฟซแบบโต้ตอบอื่นๆ อีกมากมาย
![finished terrarium](../../../../translated_images/terrarium-final.0920f16e87c13a84cd2b553a5af9a3ad1cffbd41fbf8ce715d9e9c43809a5e2c.th.png)
---
## ความท้าทาย GitHub Copilot Agent 🚀
ใช้โหมด Agent เพื่อทำความท้าทายต่อไปนี้ให้สำเร็จ:
**คำอธิบาย:** ปรับปรุงโครงการสวนขวดโดยเพิ่มฟังก์ชันการรีเซ็ตที่คืนต้นไม้ทั้งหมดไปยังตำแหน่งเดิมด้วยแอนิเมชันที่ราบรื่น
**คำสั่ง:** สร้างปุ่มรีเซ็ตที่เมื่อคลิกแล้วจะทำให้ต้นไม้ทั้งหมดเคลื่อนกลับไปยังตำแหน่งเดิมในแถบด้านข้างด้วยแอนิเมชัน CSS ที่ราบรื่น ฟังก์ชันควรเก็บตำแหน่งเดิมเมื่อหน้าโหลดและเปลี่ยนต้นไม้กลับไปยังตำแหน่งเหล่านั้นอย่างราบรื่นภายใน 1 วินาทีเมื่อกดปุ่มรีเซ็ต
เรียนรู้เพิ่มเติมเกี่ยวกับ [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) ที่นี่
## 🚀 ความท้าทายเพิ่มเติม: ขยายทักษะของคุณ
พร้อมที่จะยกระดับสวนขวดของคุณไปอีกขั้นหรือยัง? ลองเพิ่มฟังก์ชันเหล่านี้:
**การขยายความคิดสร้างสรรค์:**
- **ดับเบิลคลิก** ที่ต้นไม้เพื่อให้มันอยู่ด้านหน้า (การจัดการ z-index)
- **เพิ่มการตอบสนองทางภาพ** เช่นแสงเรืองรองเล็กน้อยเมื่อเลื่อนเมาส์ไปที่ต้นไม้
- **กำหนดขอบเขต** เพื่อป้องกันไม่ให้ต้นไม้ถูกลากออกนอกสวนขวด
- **สร้างฟังก์ชันบันทึก** ที่จดจำตำแหน่งต้นไม้โดยใช้ localStorage
- **เพิ่มเสียงเอฟเฟกต์** เมื่อหยิบและวางต้นไม้
> 💡 **โอกาสในการเรียนรู้**: ความท้าทายแต่ละข้อจะช่วยให้คุณเรียนรู้แง่มุมใหม่ๆ เกี่ยวกับการจัดการ DOM การจัดการเหตุการณ์ และการออกแบบประสบการณ์ผู้ใช้
## แบบทดสอบหลังการบรรยาย
[แบบทดสอบหลังการบรรยาย](https://ff-quizzes.netlify.app/web/quiz/20)
## ทบทวนและศึกษาด้วยตัวเอง: เพิ่มพูนความเข้าใจของคุณ
คุณได้เชี่ยวชาญพื้นฐานของการจัดการ DOM และ closures แล้ว แต่ยังมีสิ่งที่ต้องสำรวจอีกมากมาย! นี่คือเส้นทางบางส่วนเพื่อขยายความรู้และทักษะของคุณ
### วิธีการลากและวางทางเลือก
เราใช้ pointer events เพื่อความยืดหยุ่นสูงสุด แต่การพัฒนาเว็บมีวิธีการหลายแบบ:
| วิธีการ | เหมาะสำหรับ | คุณค่าการเรียนรู้ |
|---------|--------------|--------------------|
| [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) | การอัปโหลดไฟล์ โซนลากที่เป็นทางการ | ทำความเข้าใจความสามารถของเบราว์เซอร์โดยธรรมชาติ |
| [Touch Events](https://developer.mozilla.org/docs/Web/API/Touch_events) | การโต้ตอบเฉพาะมือถือ | รูปแบบการพัฒนาสำหรับมือถือเป็นหลัก |
| คุณสมบัติ CSS `transform` | แอนิเมชันที่ราบรื่น | เทคนิคการเพิ่มประสิทธิภาพ |
### หัวข้อการจัดการ DOM ขั้นสูง
**ขั้นตอนต่อไปในเส้นทางการเรียนรู้ของคุณ:**
- **Event delegation**: การจัดการเหตุการณ์อย่างมีประสิทธิภาพสำหรับองค์ประกอบหลายตัว
- **Intersection Observer**: การตรวจจับเมื่อองค์ประกอบเข้าสู่/ออกจากมุมมอง
- **Mutation Observer**: การเฝ้าดูการเปลี่ยนแปลงในโครงสร้าง DOM
- **Web Components**: การสร้างองค์ประกอบ UI ที่นำกลับมาใช้ใหม่ได้และมีการห่อหุ้ม
- **แนวคิด Virtual DOM**: ทำความเข้าใจว่ากรอบงานปรับปรุงการอัปเดต DOM อย่างไร
### แหล่งข้อมูลสำคัญสำหรับการเรียนรู้ต่อเนื่อง
**เอกสารทางเทคนิค:**
- [MDN Pointer Events Guide](https://developer.mozilla.org/docs/Web/API/Pointer_events) - คู่มือการอ้างอิง pointer event อย่างละเอียด
- [W3C Pointer Events Specification](https://www.w3.org/TR/pointerevents1/) - เอกสารมาตรฐานอย่างเป็นทางการ
- [JavaScript Closures Deep Dive](https://developer.mozilla.org/docs/Web/JavaScript/Closures) - รูปแบบ closure ขั้นสูง
**ความเข้ากันได้ของเบราว์เซอร์:**
- [CanIUse.com](https://caniuse.com/) - ตรวจสอบการรองรับฟีเจอร์ในเบราว์เซอร์ต่างๆ
- [MDN Browser Compatibility Data](https://github.com/mdn/browser-compat-data) - ข้อมูลความเข้ากันได้โดยละเอียด
**โอกาสในการฝึกฝน:**
- **สร้าง** เกมปริศนาโดยใช้กลไกการลากที่คล้ายกัน
- **สร้าง** กระดานงานที่มีการจัดการงานแบบลากและวาง
- **ออกแบบ** แกลเลอรีภาพที่สามารถจัดเรียงรูปภาพได้ด้วยการลาก
- **ทดลอง** กับท่าทางสัมผัสสำหรับอินเทอร์เฟซมือถือ
> 🎯 **กลยุทธ์การเรียนรู้**: วิธีที่ดีที่สุดในการทำให้แนวคิดเหล่านี้มั่นคงคือการฝึกฝน ลองสร้างรูปแบบต่างๆ ของอินเทอร์เฟซที่สามารถลากได้ แต่ละโครงการจะสอนสิ่งใหม่ๆ เกี่ยวกับการโต้ตอบของผู้ใช้และการจัดการ DOM
## งานที่ได้รับมอบหมาย
[ทำงานเพิ่มเติมกับ DOM](assignment.md)
---
**ข้อจำกัดความรับผิดชอบ**:
เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลโดยอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้