Javaบทช่วยสอนสคริปต์ DOM พร้อมตัวอย่าง
⚡ สรุปอย่างชาญฉลาด
JavaScript DOM และ Events ช่วยให้สคริปต์ควบคุมเว็บเพจได้อย่างเต็มที่ ทำให้โค้ดสามารถอ่าน เปลี่ยนแปลง เพิ่ม หรือลบองค์ประกอบใดๆ ที่เบราว์เซอร์แสดงผล จากนั้นตอบสนองได้ทันทีเมื่อผู้เยี่ยมชมคลิก พิมพ์ หรือเลื่อนหน้าจอ

DOM คืออะไร Javaสคริปต์?
Javaสคริปต์สามารถเข้าถึงองค์ประกอบทั้งหมดในเว็บเพจโดยใช้ Document Object Model (DOM) อันที่จริง เว็บเบราว์เซอร์จะสร้าง DOM ของเว็บเพจเมื่อโหลดหน้าเว็บ โมเดล DOM ถูกสร้างขึ้นเป็นโครงสร้างแบบต้นไม้ของอ็อบเจ็กต์ดังนี้:
แต่ละองค์ประกอบ HTML บนหน้าเว็บจะกลายเป็นโหนดภายในโครงสร้างต้นไม้นี้ เบราว์เซอร์จะจัดกลุ่มโหนด DOM ออกเป็นประเภทหลักๆ สองสามประเภท ได้แก่ โหนดองค์ประกอบ ซึ่งแทนแท็กต่างๆ เช่น <p> <p> or <div>โหนดข้อความจะเก็บคำที่มองเห็นได้ภายในแท็ก ส่วนโหนดแอตทริบิวต์จะเก็บค่าต่างๆ เช่น id or classและโหนดความคิดเห็นจะเก็บรักษาไว้ <!-- --> มาร์กอัป โหนดบนสุดจะเป็นเสมอ document วัตถุนั้น และองค์ประกอบอื่นๆ ทั้งหมดล้วนสืบทอดมาจากวัตถุนั้น
โดยใช้ DOM Javaต้นฉบับ สามารถทำงานได้หลายอย่าง เช่น สร้างองค์ประกอบและคุณลักษณะใหม่ เปลี่ยนแปลงองค์ประกอบและคุณลักษณะที่มีอยู่ และแม้กระทั่งลบองค์ประกอบและคุณลักษณะที่มีอยู่ Javaสคริปต์ยังสามารถตอบสนองต่อเหตุการณ์ที่มีอยู่และสร้างเหตุการณ์ใหม่บนหน้าเว็บได้อีกด้วย ส่วนด้านล่างจะอธิบายวิธีการเลือกองค์ประกอบ การแก้ไของค์ประกอบ และการเชื่อมต่อตัวจัดการเหตุการณ์ พร้อมตัวอย่างที่ใช้งานได้จริง
สิ่งสำคัญที่ควรจำไว้คือ DOM ไม่เหมือนกับไฟล์ HTML ต้นฉบับ HTML ที่เบราว์เซอร์ดาวน์โหลดเป็นข้อความคงที่ แต่ DOM ที่สร้างขึ้นจากข้อความนั้นเป็นแบบจำลองวัตถุแบบไดนามิกที่มีชีวิตชีวา การเปลี่ยนแปลงใดๆ ก็ตาม Javaสคริปต์ที่เขียนลงใน DOM จะแสดงผลบนหน้าจอทันที โดยไม่ต้องแก้ไขไฟล์ HTML ต้นฉบับเลย
วิธีการเลือกองค์ประกอบ DOM ใน Javaต้นฉบับ
ก่อน Javaสคริปต์สามารถเปลี่ยนแปลงอะไรก็ได้บนหน้าเว็บ แต่ก่อนอื่นต้องเลือกโหนดเป้าหมายก่อน DOM มีวิธีการเลือกหลายวิธี ซึ่งแต่ละวิธีเหมาะสมกับสถานการณ์ที่แตกต่างกัน:
- getElementById(): ส่งคืนองค์ประกอบเดียวที่ตรงกับเงื่อนไขที่กำหนด
idคุณลักษณะ - getElementsByClassName(): ส่งคืน HTMLCollection แบบเรียลไทม์ของทุกองค์ประกอบที่มีคลาสที่กำหนด
- getElementsByTagName(): ส่งคืน HTMLCollection แบบเรียลไทม์ของทุกองค์ประกอบที่มีชื่อแท็กที่กำหนด เช่น
porli. - querySelector(): ส่งคืนองค์ประกอบแรกที่ตรงกับตัวเลือก CSS ใดๆ
- querySelectorAll(): ส่งคืน NodeList แบบคงที่ของทุกองค์ประกอบที่ตรงกับตัวเลือก CSS
ตารางด้านล่างนี้เปรียบเทียบวิธีการคัดเลือกที่ใช้กันทั่วไป 4 วิธี เพื่อให้คุณสามารถเลือกวิธีที่เหมาะสมกับงานได้
| วิธี | คืนสินค้า | ใช้ดีที่สุดเมื่อใด |
|---|---|---|
| รับ ElementById | องค์ประกอบเดียว | คุณทราบรหัสประจำตัวที่แน่นอนและไม่ซ้ำใครของเป้าหมาย |
| getElementsByClassName | คอลเลกชัน HTML สด | คุณต้องกำหนดให้ทุกองค์ประกอบมีชื่อคลาสเดียวกัน |
| ตัวเลือกแบบสอบถาม | องค์ประกอบเดี่ยว (การจับคู่ครั้งแรก) | คุณต้องการใช้ตัวเลือก CSS ใดก็ได้ ไม่ใช่แค่ id หรือ class เท่านั้น |
| ข้อความค้นหาตัวเลือกทั้งหมด | รายการโหนดคงที่ | คุณต้องวนลูปผ่านทุกการจับคู่ด้วย forEach |
querySelector และ querySelectorAll รองรับตัวเลือก CSS ที่ถูกต้องทุกรูปแบบ ทำให้เป็นตัวเลือกที่ยืดหยุ่นที่สุดสำหรับโค้ดสมัยใหม่ ลองใช้โค้ดตัวอย่างด้านล่างในหน้าเว็บที่มีองค์ประกอบ <img> อยู่ id="title" และองค์ประกอบหลายอย่างที่มี class="item":
const heading = document.getElementById("title"); const itemsByClass = document.getElementsByClassName("item"); const firstItem = document.querySelector(".item"); const allItems = document.querySelectorAll(".item"); console.log(heading.textContent, itemsByClass.length, allItems.length);
getElementById และ getElementsByClassName ยังคงทำงานได้เร็วกว่าสำหรับการค้นหาแบบง่ายๆ ในขณะที่ querySelector และ querySelectorAll เป็นที่นิยมมากกว่าเมื่อตรรกะการเลือกต้องการพลังของไวยากรณ์ CSS แบบเต็มรูปแบบ เช่น ตัวเลือกแอตทริบิวต์หรือตัวเลือกแบบสืบทอด
วิธีการแก้ไข สร้าง และลบองค์ประกอบ DOM
เมื่อเลือกองค์ประกอบแล้ว Javaสคริปต์สามารถอ่านหรือเขียนทับเนื้อหา คุณสมบัติ และแม้กระทั่งสถานะของมันในโครงสร้าง DOM ได้ โดยใช้ DOM Javaสคริปต์สามารถสร้างองค์ประกอบและคุณลักษณะใหม่ เปลี่ยนแปลงองค์ประกอบและคุณลักษณะที่มีอยู่ และลบองค์ประกอบและคุณลักษณะที่มีอยู่ทั้งหมดได้
เปลี่ยนเนื้อหาด้วย getElementById และ innerHTML
- getElementById: เพื่อเข้าถึงองค์ประกอบและคุณลักษณะที่มีการตั้งค่า ID
- innerHTML: เพื่อเข้าถึงเนื้อหาขององค์ประกอบ
ลองทำตัวอย่างนี้ด้วยตัวเอง:
<html> <head> <title>DOM!!!</title> </head> <body> <h1 id="one">Welcome</h1> <p>This is the welcome message.</p> <h2>Technology</h2> <p>This is the technology section.</p> <script type="text/javascript"> var text = document.getElementById("one").innerHTML; alert("The first heading is " + text); </script> </body> </html>
กล่องข้อความแจ้งเตือนยืนยันว่า getElementById ค้นพบหัวข้อ และ innerHTML ส่งคืนข้อความที่จัดเก็บไว้ภายในนั้น
อ่านทุกย่อหน้าด้วย getElementsByTagName
getElementsByTagName: เพื่อเข้าถึงองค์ประกอบและคุณลักษณะโดยใช้ชื่อแท็ก วิธีนี้จะส่งคืนไฟล์ แถว ของรายการทั้งหมดที่มีชื่อแท็กเดียวกัน
ลองทำตัวอย่างนี้ด้วยตัวเอง:
<html> <head> <title>DOM!!!</title> </head> <body> <h1>Welcome</h1> <p>This is the welcome message.</p> <h2>Technology</h2> <p id="second">This is the technology section.</p> <script type="text/javascript"> var paragraphs = document.getElementsByTagName("p"); alert("Content in the second paragraph is " + paragraphs[1].innerHTML); document.getElementById("second").innerHTML = "The orginal message is changed."; </script> </body> </html>
เนื่องจาก getElementsByTagName ส่งคืนคอลเลกชันที่มีชีวิต paragraphs[1] จึงหมายถึงย่อหน้าที่สองในเอกสาร และการอัปเดต innerHTML จะเขียนทับข้อความบนหน้าจอทันที
สร้างและลบองค์ประกอบด้วยฟังก์ชัน createElement และ removeChild
นอกจากจะแก้ไขสิ่งที่มีอยู่แล้วบนหน้าเว็บแล้ว DOM ยังช่วยให้คุณสามารถเพิ่มองค์ประกอบใหม่หรือลบองค์ประกอบที่มีอยู่แล้วได้:
- createElement(): สร้างโหนดองค์ประกอบใหม่ในหน่วยความจำ
- เพิ่มลูก(): เพิ่มโหนดเป็นลูกตัวสุดท้ายขององค์ประกอบหลัก
- setAttribute(): เพิ่มหรืออัปเดตแอตทริบิวต์ เช่น
classorhrefบนองค์ประกอบหนึ่ง - removeChild(): ลบโหนดลูกที่ระบุออกจากโหนดแม่
ตัวอย่างด้านล่างนี้สร้างรายการใหม่ กำหนดข้อความและคลาสให้กับรายการนั้น เพิ่มรายการใหม่เข้าไปในรายการที่มีอยู่แล้ว จากนั้นลบรายการแรกสุดออกจากรายการเดิมนั้น:
const list = document.getElementById("myList"); const newItem = document.createElement("li"); newItem.textContent = "Freshly added item"; newItem.setAttribute("class", "highlight"); list.appendChild(newItem); list.removeChild(list.firstElementChild);
เมธอด createElement() เพียงอย่างเดียวจะไม่แสดงผลใดๆ เนื่องจากโหนดนั้นยังคงอยู่ในหน่วยความจำเท่านั้น มันจะปรากฏขึ้นก็ต่อเมื่อเมธอด appendChild() หรือเมธอดที่คล้ายกัน เช่น prepend() ได้แนบมันเข้าไปในโครงสร้างต้นไม้ที่มองเห็นได้แล้ว เบราว์เซอร์สมัยใหม่ยังรองรับเมธอด node.remove() ที่สั้นกว่า ซึ่งจะลบองค์ประกอบโดยตรงโดยไม่ต้องอ้างอิงถึงองค์ประกอบแม่ ต่างจากไวยากรณ์ parent.removeChild(node) แบบเก่า
วิธีจัดการกับเหตุการณ์ต่างๆ ใน Javaต้นฉบับ
เหตุการณ์ คือ การกระทำใดๆ ที่เบราว์เซอร์สามารถตรวจจับได้ เช่น การคลิก การกดปุ่ม หรือการโหลดหน้าเว็บ คุณสามารถเพิ่มตัวจัดการเหตุการณ์ให้กับองค์ประกอบใดองค์ประกอบหนึ่งได้สองวิธีหลักๆ
แนบ Handler ด้วย onclick หรือ addEventListener
วิธีการแรกซึ่งเป็นวิธีเก่ากว่านั้น จะกำหนดฟังก์ชันให้กับคุณสมบัติ on-event ขององค์ประกอบโดยตรง:
document.getElementById(id).onclick=function()
{
lines of code to be executed
}
หรือใช้วิธี addEventListener() ที่ทันสมัยและยืดหยุ่นกว่า:
document.getElementById(id).addEventListener("click", functionname)
โดยทั่วไปแล้ว นิยมใช้ addEventListener() มากกว่า เพราะช่วยให้คุณสามารถแนบตัวจัดการเหตุการณ์มากกว่าหนึ่งตัวเข้ากับเหตุการณ์เดียวกันบนองค์ประกอบเดียวกันได้ และยังรับอาร์กิวเมนต์ที่สามซึ่งเป็นตัวเลือกสำหรับควบคุมการจับเหตุการณ์ ซึ่งจะกล่าวถึงในภายหลังในส่วนนี้ นอกจากนี้ยังใช้คู่กับ removeEventListener() ซึ่งจะถอดตัวจัดการเหตุการณ์ออกเมื่อไม่จำเป็นอีกต่อไป ซึ่งเป็นสิ่งที่คุณสมบัติ onclick ไม่สามารถทำได้ด้วยตัวเอง
HTML ยังรองรับรูปแบบที่สามอีกด้วย นั่นคือ ตัวจัดการแบบอินไลน์ที่เขียนไว้โดยตรงบนแท็ก เช่น <button onclick="clicked()">Click Me</button>ตัวจัดการแบบอินไลน์ผสมผสานมาร์กอัปกับตรรกะ ทำให้ยากต่อการบำรุงรักษาอย่างรวดเร็ว ดังนั้นส่วนใหญ่จึงใช้ตัวจัดการแบบอินไลน์ Javaนักพัฒนาสคริปต์จะสงวนฟังก์ชันนี้ไว้สำหรับการสาธิตอย่างรวดเร็ว และใช้ addEventListener() สำหรับแอปพลิเคชันจริงแทน
ลองทำดู: ตัวอย่างเหตุการณ์คลิกแบบสมบูรณ์
ลองทำตัวอย่างนี้ด้วยตัวเอง:
<html> <head> <title>DOM!!!</title> </head> <body> <input type="button" id="btnClick" value="Click Me!!" /> <script type="text/javascript"> document.getElementById("btnClick").addEventListener("click", clicked); function clicked() { alert("You clicked me!!!"); } </script> </body> </html>
การคลิกปุ่มจะเรียกใช้ฟังก์ชัน clicked() ซึ่งจะกระตุ้นการทำงานต่อไป Javaกล่องข้อความแจ้งเตือนสคริปต์ยืนยันว่าตัวจัดการเหตุการณ์ทำงานสำเร็จแล้ว
วัตถุเหตุการณ์
ตัวจัดการเหตุการณ์ทุกตัวจะได้รับอ็อบเจ็กต์เหตุการณ์เป็นอาร์กิวเมนต์แรกโดยอัตโนมัติ อ็อบเจ็กต์นี้มีรายละเอียดที่เป็นประโยชน์เกี่ยวกับสิ่งที่เกิดขึ้น รวมถึง event.target (องค์ประกอบที่การกระทำเริ่มต้นขึ้นอย่างแม่นยำ), event.type (ชื่อเหตุการณ์ เช่น "คลิก") และเมธอดต่างๆ เช่น event.preventDefault() ซึ่งจะหยุดพฤติกรรมเริ่มต้นของเบราว์เซอร์ เช่น การคลิกลิงก์หรือการส่งแบบฟอร์ม
document.getElementById("btnClick").addEventListener("click", function(event) { console.log("Clicked element: " + event.target.id); });
คุณสมบัติเพิ่มเติมอีกสองอย่างจะช่วยเสริมความสมบูรณ์ให้กับอ็อบเจ็กต์เหตุการณ์ ได้แก่ event.currentTarget โดยจะอ้างอิงถึงองค์ประกอบที่ตัวรับฟังเชื่อมต่ออยู่เสมอ แม้ว่า event.target จะรายงานว่ามีองค์ประกอบย่อยอยู่ภายในก็ตาม และ event.stopPropagation() จะหยุดเหตุการณ์ก่อนที่จะแพร่กระจายหรือดักจับต่อไป เมื่อใช้ร่วมกับ preventDefault() เครื่องมือเหล่านี้จะช่วยให้ตัวจัดการเหตุการณ์ควบคุมสิ่งที่เกิดขึ้นต่อไปได้อย่างแม่นยำ
อีเว้นท์ Bubblการรวบรวม การบันทึก และการมอบหมาย
เมื่อเหตุการณ์เกิดขึ้นกับองค์ประกอบที่ซ้อนกัน เหตุการณ์นั้นจะไม่หยุดอยู่แค่นั้น ใน... เดือด ในขั้นตอนนี้ เหตุการณ์จะเคลื่อนที่ขึ้นจากองค์ประกอบเป้าหมาย ผ่านบรรพบุรุษแต่ละตัว ไปจนถึงเอกสาร ในขั้นตอนนี้ จับ ในขั้นตอนที่ทำงานก่อนและเป็นแบบเลือกเข้าร่วม เหตุการณ์จะเดินทางลงมาจากเอกสารไปยังเป้าหมายก่อนที่การแพร่กระจายจะเริ่มต้นขึ้น ผ่าน {capture: true} ใช้เป็นอาร์กิวเมนต์ที่สามของ addEventListener() เพื่อรับฟังในระหว่างขั้นตอนการบันทึก
การมอบหมายงาน ใช้ประโยชน์จากกลไกการกระจายเหตุการณ์ (bubbling): แทนที่จะแนบตัวรับฟังเหตุการณ์แยกต่างหากให้กับองค์ประกอบย่อยแต่ละตัว คุณจะแนบตัวรับฟังเหตุการณ์เพียงตัวเดียวให้กับองค์ประกอบหลักที่ใช้ร่วมกัน และตรวจสอบ event.target เพื่อพิจารณาว่าองค์ประกอบย่อยใดเป็นตัวกระตุ้นเหตุการณ์นั้น วิธีนี้ใช้หน่วยความจำน้อยลงและครอบคลุมองค์ประกอบที่เพิ่มเข้ามาในหน้าเว็บในภายหลังโดยอัตโนมัติ
const list = document.getElementById("myList"); list.addEventListener("click", function(event) { if (event.target.tagName === "LI") { alert("You clicked " + event.target.textContent); } });
การมอบหมายสิทธิ์ (Delegation) ร่วมกับการส่งต่อสิทธิ์ (Bubbling) และการดักจับสิทธิ์ (Capturing) ช่วยให้คุณควบคุมวิธีการทำงานของ DOM และเหตุการณ์ต่างๆ ได้อย่างละเอียด Javaสคริปต์จะตอบสนองเมื่อหน้าเว็บมีการโต้ตอบมากขึ้น

