บทนำ - JavaScript ทำให้ Web มีชีวิต
JavaScript คือภาษาโปรแกรมที่ทำให้ HTML page เต็มไปด้วยชีวิต (interactive) โดยไม่ต้อง refresh หน้า ระบบสามารถตอบสนองต่อการกระทำของผู้ใช้ทันที:
ความสามารถหลักของ JavaScript
| ความสามารถ | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| DOM Manipulation | เปลี่ยนเนื้อหา สไตล์ attributes ของ HTML elements | เปลี่ยนข้อความ, เปลี่ยนสี, ซ่อน/แสดง elements |
| Event Handling | ตอบสนองต่อการกระทำของผู้ใช้ (click, input, mouse) | ปุ่ม click, การพิมพ์, การไล่หา |
| Form Validation | ตรวจสอบข้อมูลฟอร์มก่อนส่งไปยัง server | ตรวจสอบ email, password, ข้อมูลที่จำเป็น |
| Real-time Updates | อัปเดตข้อมูลแบบ real-time โดยไม่ reload | Live search, character counter, todo list |
| Animations & Effects | สร้างเอฟเฟกต์ transitions และ animations | Fade in/out, slide, toggle menu |
| API Communication | สื่อสารกับ server โดยใช้ Fetch API | ดึงข้อมูลจาก backend, ส่งข้อมูล |
วิธีการเขียน JavaScript - 3 วิธี
JavaScript สามารถเขียนได้ 3 วิธี แต่ละวิธีมีข้อดี ข้อเสีย ลำดับความเหมาะสม:
1. Inline JavaScript (ไม่แนะนำ) ❌
Inline หมายถึง เขียน JavaScript โดยตรงในแอตทริบิวต์
HTML เช่น onclick:
<!-- HTML -->
<button onclick="alert('Hello!')">Click me</button>
<a href="javascript:alert('Clicked!)">Link</a>
<!-- ปัญหา: -->
<!-- 1. ผสม HTML และ JavaScript เข้าด้วยกัน (ยากต่อการรักษา) -->
<!-- 2. Code ซ้ำได้ถ้าต้องใช้ onclick กับหลายปุ่ม -->
<!-- 3. ยากต่อการ debug และ test -->
<!-- 4. ไม่ได้มาตรฐาน (best practice) -->
2. Internal JavaScript (ใช้ได้) ⚠️
Internal หมายถึง เขียน JavaScript ในแท็ก
<script> ภายในไฟล์ HTML เดียวกัน:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome</h1>
<button id="myBtn">Click me</button>
<!-- JavaScript ที่นี่ (ท้าย body) -->
<script>
// JavaScript code ที่นี่
const btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
alert("Hello from Internal JS!");
});
</script>
</body>
</html>
- ทำงานได้ดี เหมาะสำหรับ page ที่ไม่ซับซ้อน
- เห็นการเชื่อมโยง HTML และ JS อย่างชัดเจน
- ไม่ต้องจัดการไฟล์หลายไฟล์
- HTML และ JS ผสมกัน ยากต่อ maintenance
- ไม่สามารถใช้ code เดียวกันในหน้าอื่นได้
- Browser ต้องเรียนรู้ script ทุกครั้งที่โหลด page
3. External JavaScript (แนะนำ) ✅
External หมายถึง เขียน JavaScript ในไฟล์แยกต่างหาก (.js file) แล้ว link เข้ามา:
📄 index.html:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome</h1>
<button id="myBtn">Click me</button>
<!-- Link ไฟล์ JavaScript ที่ท้าย body -->
<script src="script.js" defer></script>
<!--
defer = รอให้ HTML โหลดเสร็จก่อนรัน script
ทำให้ page โหลดเร็วขึ้น
-->
</body>
</html>
📄 script.js (ไฟล์ JavaScript แยก):
// script.js
const btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
alert("Hello from External JS!");
console.log("Button clicked!");
});
- HTML และ JS แยกกัน โค้ดสะอาด และ organized
- สามารถใช้ script.js ในหลาย HTML pages
- Browser cache ไฟล์ .js ได้ ทำให้ page โหลดเร็วขึ้น
- ง่ายต่อ debugging และ testing
- เป็นมาตรฐาน (best practice) ที่มืองานปกติใช้
- ต้องจัดการไฟล์หลายไฟล์
- ต้องระวัง path ของไฟล์ .js
- ต้องใช้ web server เพื่อทดสอบ (localhost)
- 🏭 Professional: ใช้ใน production ทั่วไป
- 📦 Reusable: ใช้ในหลาย pages
- ⚡ Performance: Browser cache ไฟล์
- 🧹 Maintainable: โค้ดสะอาดและจัดเก็บดี
DOM Manipulation - การจัดการเนื้อหา HTML
DOM (Document Object Model) คือ tree structure ของ HTML elements ที่ JavaScript สามารถเข้าถึงและแก้ไขได้ DOM Manipulation ทำให้เราสามารถ:
- 🔍 เลือก (Select) elements จาก HTML
- ✏️ แก้ไข (Modify) เนื้อหา, สไตล์, attributes
- ➕ สร้าง (Create) elements ใหม่
- ❌ ลบ (Remove) elements
1. การเลือก Elements - Selection
ก่อนแก้ไขใด ๆ ต้องเลือก element ก่อน มีหลายวิธี:
<!-- HTML -->
<div id="header" class="container">
<h1>Welcome</h1>
<p class="text">Hello World</p>
<p class="text">Hello JavaScript</p>
</div>
<p id="footer" class="text highlight">Footer</p>
⭐ วิธีเลือก Elements (เรียงลำดับความเหมาะสม):
| วิธี | ไวยากรณ์ | ผลลัพธ์ | หมายเหตุ |
|---|---|---|---|
| querySelector ⭐ | document.querySelector(".class") |
Element แรกที่ตรง | ใช้ CSS selector, ใช้ได้ทั่วไป |
| querySelectorAll | document.querySelectorAll("p") |
NodeList ทั้งหมด | ได้หลาย elements ต้อง loop |
| getElementById | document.getElementById("myId") |
Element หนึ่ง | เก่า แต่ใช้ยาว |
| getElementsByClassName | document.getElementsByClassName("class") |
HTMLCollection | เก่า ต้องจำ class name ทั้งหมด |
| getElementsByTagName | document.getElementsByTagName("p") |
HTMLCollection | เก่า ใช้ tag name |
💡 ตัวอย่างจริง:
<script>
// 1. querySelector - แนะนำ (ใช้ CSS selector)
const header = document.querySelector("#header");
const firstPara = document.querySelector(".text");
const heading = document.querySelector("#header h1");
// 2. querySelectorAll - ได้หลาย elements
const allParas = document.querySelectorAll(".text"); // NodeList [p, p, p]
allParas.forEach(p => {
console.log(p.textContent);
});
// 3. getElementById - เก่า แต่เร็ว
const footer = document.getElementById("footer");
// 4. getElementsByClassName - เก่า
const textElements = document.getElementsByClassName("text");
// 5. getElementsByTagName - เก่า
const allPs = document.getElementsByTagName("p");
console.log("querySelector result:", firstPara);
console.log("querySelectorAll result:", allParas);
</script>
- ใช้ CSS selectors (คุ้นเคยกับ CSS แล้ว)
- Flexible: ID, class, tag, attribute ได้ทั้งหมด
- โค้ดสั้นและอ่านง่าย
2. การแก้ไขเนื้อหา - Modify Content
มี 4 วิธีเปลี่ยนเนื้อหาข้อความและ HTML:
<!-- HTML -->
<h1 id="title">Old Title</h1>
<p id="description">Old description</p>
<input id="myInput" type="text" value="Old value">
<button id="updateBtn">Update Content</button>
<script>
const updateBtn = document.getElementById("updateBtn");
updateBtn.addEventListener("click", () => {
// 1. textContent - เปลี่ยนเนื้อหาเป็น text (ปลอดภัย)
// ❌ ไม่รับ HTML tags
document.getElementById("title").textContent = "New Title";
console.log(document.getElementById("title").textContent); // "New Title"
// 2. innerText - เหมือน textContent แต่ต้องการ rendering
// ใช้ textContent ดีกว่า
document.getElementById("description").innerText = "New description";
// 3. innerHTML - เปลี่ยนเนื้อหา + รับ HTML tags (อันตรายถ้าได้ user input!)
// ⚠️ ระวัง XSS vulnerability!
document.getElementById("description").innerHTML =
"<strong>Bold text</strong> <em>Italic</em>";
// 4. value - สำหรับ form elements (input, textarea, select)
// ใช้เมื่อต้องการเปลี่ยนค่าที่อยู่ในตัวเลือก
const input = document.querySelector("#myInput");
input.value = "New value";
console.log(input.value); // "New value"
});
</script>
// ปลอดภัยและเร็ว
element.textContent = "Hello";
// อันตราย! ถ้า userInput = "<img src=x onerror=alert('XSS')>"
element.innerHTML = userInput; // จะรัน JavaScript!
// ปลอดภัย:
element.textContent = userInput; // แสดงเป็น text ธรรมชาติ
3. การแก้ไขโครงสร้าง - Create, Add, Remove Elements
สร้าง elements ใหม่ เพิ่มลง DOM หรือลบออก:
<!-- HTML -->
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
<button id="addBtn">Add Item</button>
<script>
const addBtn = document.getElementById("addBtn");
let itemCount = 2;
// ===== สร้าง (Create) =====
addBtn.addEventListener("click", () => {
// 1. createElement - สร้าง element ใหม่ (ยังไม่อยู่ใน DOM)
const newItem = document.createElement("li");
// 2. ตั้งค่าเนื้อหา
itemCount++;
newItem.textContent = `Item ${itemCount}`;
newItem.style.color = "blue";
// 3. appendChild - เพิ่ม element ลงไปที่ท้ายสุด
document.getElementById("list").appendChild(newItem);
});
// ===== ลบ (Remove) =====
// วิธี 1: remove() - ลบ element ออกจาก DOM
const firstItem = document.querySelector("li");
firstItem.remove(); // ไม่มี element นี้อีกแล้ว
// วิธี 2: removeChild - ลบ child element
const parent = document.getElementById("list");
if (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
// ===== เพิ่มที่ตำแหน่งอื่น (Insert) =====
// insertBefore - ใส่ก่อนหน้า element ที่ระบุ
const newFirst = document.createElement("li");
newFirst.textContent = "New First Item";
parent.insertBefore(newFirst, parent.firstChild);
// ===== แทนที่ (Replace) =====
const replacement = document.createElement("li");
replacement.textContent = "Replaced Item";
const oldItem = document.querySelector("li");
oldItem.replaceWith(replacement);
</script>
4. การแก้ไข CSS Classes - classList
ใช้ classList เพื่อเพิ่ม/ลบ/สลับ CSS classes:
<!-- HTML -->
<div id="box" class="box">Box</div>
<button id="toggleBtn">Toggle Style</button>
<style>
.box {
width: 100px;
height: 100px;
background: blue;
transition: all 0.3s ease;
}
.highlight { background: red; }
.big { width: 200px; height: 200px; }
.hidden { display: none; }
</style>
<script>
const box = document.getElementById("box");
const btn = document.getElementById("toggleBtn");
// 1. classList.add - เพิ่ม class ใหม่
box.classList.add("highlight");
console.log(box.className); // "box highlight"
// 2. classList.remove - ลบ class ออก
box.classList.remove("highlight");
console.log(box.className); // "box"
// 3. classList.toggle - สลับ class (มี ให้ลบ, ไม่มี ให้เพิ่ม)
btn.addEventListener("click", () => {
box.classList.toggle("highlight");
// Click ครั้ง 1: เพิ่ม highlight
// Click ครั้ง 2: ลบ highlight
// Click ครั้ง 3: เพิ่ม highlight
// ... สลับไปมา
});
// 4. classList.contains - ตรวจสอบว่ามี class หรือไม่
if (box.classList.contains("highlight")) {
console.log("Box has highlight class");
} else {
console.log("Box does not have highlight class");
}
// 5. classList.replace - แทนที่ class
box.classList.replace("box", "box big");
// ❌ ระวัง: className เปลี่ยน class ทั้งหมด (อันตราย!)
// box.className = "box big"; // ทำให้ class เก่าหมดไป
</script>
- ✅ classList.add/remove/toggle เปลี่ยนเฉพาะ class ที่ต้อง
- ❌ className เปลี่ยน class ทั้งหมด (เสี่ยงสูญเสีย class เก่า)
5. การแก้ไข Attributes - เปลี่ยนแอตทริบิวต์
แอตทริบิวต์คือ meta information ของ element (src, href, alt, data-*, etc.):
<!-- HTML -->
<img id="myImage" src="old.jpg" alt="Old Image" title="Image">
<a id="myLink" href="#">Click</a>
<input id="myInput" type="text" disabled>
<script>
const img = document.getElementById("myImage");
const link = document.getElementById("myLink");
const input = document.getElementById("myInput");
// ===== วิธีทั่วไป (ใช้กับ attribute ใด ๆ) =====
// 1. setAttribute - ตั้งค่า attribute
img.setAttribute("src", "new.jpg");
img.setAttribute("alt", "New Image");
img.setAttribute("data-category", "landscape");
// 2. getAttribute - อ่าน attribute
console.log(img.getAttribute("src")); // "new.jpg"
console.log(img.getAttribute("alt")); // "New Image"
console.log(img.getAttribute("data-category")); // "landscape"
// 3. hasAttribute - ตรวจสอบว่ามี attribute หรือไม่
if (img.hasAttribute("alt")) {
console.log("Image has alt attribute");
}
// 4. removeAttribute - ลบ attribute ออก
img.removeAttribute("title");
input.removeAttribute("disabled");
// ===== Shortcut properties (ใช้ได้เร็ว) =====
// สำหรับ common attributes ใช้ shortcut properties ได้:
link.href = "https://example.com";
img.src = "another.jpg";
input.value = "New value";
input.disabled = true; // สำหรับ boolean attributes
// ===== ตัวอย่างจริง =====
// เปลี่ยน link attributes
link.setAttribute("target", "_blank");
link.setAttribute("rel", "noopener");
// ใช้ data attributes (เก็บข้อมูล custom)
const element = document.createElement("div");
element.setAttribute("data-user-id", "123");
element.setAttribute("data-action", "delete");
console.log(element.getAttribute("data-user-id")); // "123"
</script>
6. การแก้ไข CSS Styles - เปลี่ยนสีและลักษณะ
มี 2 วิธีเปลี่ยน CSS style ผ่าน JavaScript:
<!-- HTML -->
<div id="box">Box</div>
<button id="styleBtn">Change Style</button>
<script>
const box = document.getElementById("box");
const btn = document.getElementById("styleBtn");
// ===== วิธี 1: style property (ครั้งละ 1 property) =====
box.style.width = "200px";
box.style.height = "200px";
box.style.backgroundColor = "blue"; // ⚠️ camelCase!
box.style.border = "2px solid black";
box.style.borderRadius = "10px";
box.style.transition = "all 0.3s ease";
// หมายเหตุ: CSS ใช้ kebab-case แต่ JS ใช้ camelCase
// CSS: background-color → JS: backgroundColor
// CSS: border-radius → JS: borderRadius
// CSS: font-size → JS: fontSize
// ===== วิธี 2: cssText (เปลี่ยน CSS หลายตัวพร้อมกัน) =====
btn.addEventListener("click", () => {
box.style.cssText = `
width: 300px;
height: 300px;
background-color: red;
border: 3px solid green;
border-radius: 15px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
`;
});
// ===== ตัวอย่างเพิ่มเติม =====
// เปลี่ยนสี
box.style.color = "white";
// เปลี่ยนขนาด font
box.style.fontSize = "20px";
box.style.fontWeight = "bold";
// เปลี่ยน padding/margin
box.style.padding = "20px";
box.style.margin = "10px auto";
// Display type
box.style.display = "flex";
box.style.justifyContent = "center";
box.style.alignItems = "center";
</script>
- ❌ box.style.background-color (ผิด)
- ✅ box.style.backgroundColor (ถูก)
Event Handling - ตอบสนองต่อการกระทำผู้ใช้
Event คือ การกระทำของผู้ใช้ (click, type, scroll, etc.) เมื่อ event เกิดขึ้น เราสามารถรัน JavaScript code ได้ทันที:
1. ประเภท Events ทั่วไป
📋 ตารางเปรียบเทียบ Events:
| ประเภท Event | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| Mouse Events | ||
| click | คลิก element | ปุ่ม, link, div |
| dblclick | double-click element | เลือกข้อความ |
| mouseover / mouseout | เมื่อเมาส์ผ่าน element | Hover effects |
| mouseenter / mouseleave | เหมือน mouseover แต่ไม่ bubble | ตัวอักษรที่มีขนาดกว่า |
| Keyboard Events | ||
| keydown | เมื่อกดปุ่ม (ยังไม่ปล่อย) | ตัวอักษรลาดเคราะห์ |
| keyup | เมื่อปล่อยปุ่ม | ตรวจสอบการกด Enter |
| keypress | เมื่อพิมพ์ตัวอักษร (เก่า) | ส่ง form ด้วย Enter |
| Form Events | ||
| input | เมื่อพิมพ์ในช่องข้อมูล (ทันที) | Live search, character counter |
| change | เมื่อเปลี่ยนค่า แล้วหลุดโฟกัส | เมนูเลือก (select) |
| focus / blur | ได้/เสีย focus | ไฮไลต์ช่องข้อมูล |
| submit | ส่ง form | ตรวจสอบข้อมูล ก่อนส่ง |
| Document/Window Events | ||
| load | เมื่อ page/image โหลดเสร็จ | ตั้งค่าเริ่มต้น |
| scroll | เมื่อ scroll page | Lazy loading, infinite scroll |
| resize | เมื่อ resize window | Responsive design |
💡 ตัวอย่างจริง:
<!-- HTML -->
<button id="btn">Click me</button>
<input id="input" type="text" placeholder="Type...">
<div id="box">Hover me</div>
<select id="select">
<option value="">Choose...</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
<script>
// ===== Mouse Events =====
document.getElementById("btn").addEventListener("click", (event) => {
console.log("✅ Button clicked!");
console.log("target:", event.target); // องค์ประกอบที่ถูกคลิก
});
// ===== Keyboard Events =====
document.getElementById("input").addEventListener("input", (event) => {
console.log("🔤 Input value:", event.target.value); // ทันที ทุกครั้งที่พิมพ์
});
document.getElementById("input").addEventListener("change", (event) => {
console.log("✏️ Changed to:", event.target.value); // หลังจากหลุดโฟกัส
});
document.getElementById("input").addEventListener("keydown", (event) => {
console.log("📍 Key down:", event.key); // "a", "Enter", "Backspace", etc.
if (event.key === "Enter") {
console.log("👉 User pressed Enter!");
}
});
// ===== Focus Events =====
document.getElementById("input").addEventListener("focus", () => {
console.log("🎯 Input focused");
});
document.getElementById("input").addEventListener("blur", () => {
console.log("❌ Input blurred");
});
// ===== Hover Events =====
document.getElementById("box").addEventListener("mouseover", () => {
console.log("🖱️ Mouse over box");
event.target.style.backgroundColor = "yellow";
});
document.getElementById("box").addEventListener("mouseout", () => {
console.log("🖱️ Mouse out of box");
event.target.style.backgroundColor = "";
});
// ===== Form Select Change =====
document.getElementById("select").addEventListener("change", (event) => {
console.log("Selected:", event.target.value);
});
// ===== Window Events =====
window.addEventListener("scroll", () => {
console.log("Scrolling...");
});
window.addEventListener("resize", () => {
console.log("Window resized to:", window.innerWidth, "x", window.innerHeight);
});
</script>
2. addEventListener - วิธีที่ถูก ✅
addEventListener คือวิธี modern และถูก วิธี เพราะสามารถลบ listener ได้:
<!-- HTML -->
<button id="myBtn">Click me</button>
<button id="removeBtn">Remove Listener</button>
<script>
const btn = document.getElementById("myBtn");
const removeBtn = document.getElementById("removeBtn");
// ✅ ดี - สามารถลบ listener ได้
function handleClick(event) {
console.log("Button clicked!");
}
// เพิ่ม listener
btn.addEventListener("click", handleClick);
// ลบ listener (ต้องใช้ชื่อฟังก์ชันเดียวกัน)
removeBtn.addEventListener("click", () => {
btn.removeEventListener("click", handleClick);
console.log("Listener removed!");
});
// ✅ Arrow function (สะดวก แต่ลบไม่ได้)
btn.addEventListener("dblclick", () => {
console.log("Double clicked!");
});
// ===== Event Object =====
btn.addEventListener("click", (event) => {
console.log(event.type); // "click"
console.log(event.target); // element ที่ถูกคลิก
console.log(event.clientX); // พิกัด X ของเมาส์
console.log(event.clientY); // พิกัด Y ของเมาส์
console.log(event.key); // สำหรับ keyboard events
});
</script>
<!-- ❌ ไม่ดี - Inline event handlers (เก่า) -->
<button onclick="alert('Hello')">Click</button>
<!-- ✅ ดี - addEventListener (modern) -->
<button id="btn">Click</button>
<script>
document.getElementById("btn").addEventListener("click", () => {
alert("Hello");
});
</script>
3. Event Delegation - สำหรับ Dynamic Elements
Event Delegation ใช้เมื่อต้องจัดการ events สำหรับ elements ที่สร้างภายหลัง (dynamic):
<!-- HTML -->
<ul id=\"list\">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<button id=\"addBtn\">Add Item</button>
<script>
// ❌ ไม่ดี - เพิ่ม listener ให้กับทุกๆ item
// document.querySelectorAll(\"li\").forEach(li => {
// li.addEventListener(\"click\", handler);
// });
// ปัญหา: item ใหม่ที่สร้างภายหลังจะไม่มี listener
// ✅ ดี - ใช้ Event Delegation
const list = document.getElementById("list");
// เพิ่ม listener ให้กับ parent (ul)
list.addEventListener("click", (event) => {
// ตรวจสอบว่า element ที่ถูกคลิกเป็น li หรือไม่
if (event.target.tagName === "LI") {
console.log("Item clicked:", event.target.textContent);
event.target.style.backgroundColor = "yellow";
}
});
// สร้าง item ใหม่
document.getElementById("addBtn").addEventListener("click", () => {
const newItem = document.createElement("li");
newItem.textContent = `Item ${document.querySelectorAll("li").length + 1}`;
list.appendChild(newItem);
// ไม่ต้องเพิ่ม listener ใหม่ เพราะ event delegation จัดการให้
});
</script>
- 📦 Elements ใหม่อัตโนมัติมี listener (ไม่ต้องทำซ้ำ)
- ⚡ ประสิทธิภาพดี (listener เดียว แทนหลาย ๆ listener)
- 🧹 โค้ดสะอาด และ maintainable
Form Validation - ตรวจสอบข้อมูลก่อนส่ง
Form Validation ตรวจสอบให้แน่ใจว่าผู้ใช้ใส่ข้อมูลถูกต้องก่อนส่งไปยัง server วิธีนี้ช่วยป้องกัน invalid data และให้ user experience ที่ดี:
ประเภท Validation
| ประเภท | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| HTML Validation | ใช้ attributes เช่น required, type="email" | <input type="email" required> |
| Client Validation | ตรวจสอบด้วย JavaScript (ก่อนส่ง server) | ตรวจสอบความยาว, format, etc. |
| Server Validation 🔑 | ตรวจสอบฝั่ง backend (ที่เหมาะที่สุด) | Database validation, business rules |
ตัวอย่าง: Contact Form Validation
<!-- HTML -->
<form id="contactForm">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" name="name" placeholder="Enter your name">
<span class="error" id="nameError"></span>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="your@email.com">
<span class="error" id="emailError"></span>
</div>
<div class="form-group">
<label for="message">Message:</label>
<textarea id="message" name="message" rows="5" placeholder="Your message..."></textarea>
<span class="error" id="messageError"></span>
</div>
<div class="form-group">
<label for="age">Age:</label>
<input type="number" id="age" name="age" min="18" max="100">
<span class="error" id="ageError"></span>
</div>
<button type="submit">Submit</button>
<button type="reset">Clear</button>
</form>
<style>
.form-group {
margin: 15px 0;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, textarea {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
input.error-input, textarea.error-input {
border: 2px solid #e74c3c;
background-color: #fadbd8;
}
.error {
color: #e74c3c;
font-size: 0.9em;
display: block;
margin-top: 3px;
}
button {
padding: 10px 20px;
margin: 5px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #2980b9;
}
</style>
<script>
const form = document.getElementById(\"contactForm\");
// ===== Helper functions =====
function showError(inputId, errorId, message) {
const input = document.getElementById(inputId);
const error = document.getElementById(errorId);
error.textContent = message;
input.classList.add(\"error-input\");
}
function clearError(inputId, errorId) {
const input = document.getElementById(inputId);
const error = document.getElementById(errorId);
error.textContent = "";
input.classList.remove("error-input");
}
// ===== Validation functions =====
function validateName() {
const name = document.getElementById("name").value.trim();
if (name === "") {
showError("name", "nameError", "❌ Name is required");
return false;
}
if (name.length < 3) {
showError("name", "nameError", "❌ Name must be at least 3 characters");
return false;
}
if (name.length > 50) {
showError("name", "nameError", "❌ Name must be less than 50 characters");
return false;
}
clearError("name", "nameError");
return true;
}
function validateEmail() {
const email = document.getElementById("email").value.trim();
// Regular expression for email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (email === "") {
showError("email", "emailError", "❌ Email is required");
return false;
}
if (!emailRegex.test(email)) {
showError("email", "emailError", "❌ Please enter a valid email");
return false;
}
clearError("email", "emailError");
return true;
}
function validateMessage() {
const message = document.getElementById("message").value.trim();
if (message === "") {
showError("message", "messageError", "❌ Message is required");
return false;
}
if (message.length < 10) {
showError("message", "messageError", "❌ Message must be at least 10 characters");
return false;
}
clearError("message", "messageError");
return true;
}
function validateAge() {
const age = document.getElementById("age").value;
if (age === "") {
showError("age", "ageError", "❌ Age is required");
return false;
}
if (age < 18) {
showError("age", "ageError", "❌ You must be at least 18");
return false;
}
if (age > 100) {
showError("age", "ageError", "❌ Age must be less than 100");
return false;
}
clearError("age", "ageError");
return true;
}
// ===== Real-time validation (ตรวจสอบขณะพิมพ์) =====
document.getElementById("name").addEventListener("blur", validateName);
document.getElementById("email").addEventListener("blur", validateEmail);
document.getElementById("message").addEventListener("blur", validateMessage);
document.getElementById("age").addEventListener("blur", validateAge);
// ===== Submit handler =====
form.addEventListener("submit", (event) => {
event.preventDefault(); // หยุดการส่ง form ปกติ
// ตรวจสอบทั้งหมด
const isNameValid = validateName();
const isEmailValid = validateEmail();
const isMessageValid = validateMessage();
const isAgeValid = validateAge();
// ถ้าทั้งหมดถูกต้อง ส่ง data
if (isNameValid && isEmailValid && isMessageValid && isAgeValid) {
console.log("✅ Form is valid! Sending...");
// สร้าง object data
const formData = {
name: document.getElementById("name").value,
email: document.getElementById("email").value,
message: document.getElementById("message").value,
age: document.getElementById("age").value
};
console.log("Form data:", formData);
// ส่งไปยัง server (จำลอง)
// fetch('/api/contact', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(formData)
// })
// .then(response => response.json())
// .then(data => console.log('Success:', data))
// .catch(error => console.log('Error:', error));
alert("✅ Form submitted successfully!");
form.reset(); // ล้างข้อมูล form
} else {
alert("❌ Please fix the errors");
}
});
</script>
Animation & Effects - เอฟเฟกต์และ Animation
JavaScript สามารถสร้าง animations ได้โดย toggle CSS classes หรือเปลี่ยน CSS properties ในรูปแบบ smooth transitions:
1. Toggle Menu - ซ่อน/แสดง Menu
<!-- HTML -->
<button id="menuBtn" class="menu-toggle">☰ Menu</button>
<nav id="menu" class="menu hidden">
<a href="#home">🏠 Home</a>
<a href="#about">ℹ️ About</a>
<a href="#services">🔧 Services</a>
<a href="#contact">📧 Contact</a>
</nav>
<style>
.menu {
background: #2c3e50;
color: white;
padding: 20px;
transition: all 0.3s ease; /* smooth animation */
max-height: 300px;
overflow: hidden;
opacity: 1;
}
.menu.hidden {
max-height: 0;
padding: 0 20px;
opacity: 0;
}
.menu a {
display: block;
padding: 10px 0;
text-decoration: none;
color: white;
border-bottom: 1px solid rgba(255,255,255,0.2);
}
.menu a:hover {
background: rgba(255,255,255,0.1);
padding-left: 10px;
}
</style>
<script>
const menuBtn = document.getElementById("menuBtn");
const menu = document.getElementById("menu");
menuBtn.addEventListener("click", () => {
menu.classList.toggle("hidden");
menuBtn.classList.toggle("active");
});
</script>
2. Fade In/Out - ค่อยๆ ปรากฏและหายไป
<!-- HTML -->
<div id="fadeBox" class="fade-box">
Fade in and out effect
</div>
<button id="fadeBtn">Toggle Fade</button>
<style>
.fade-box {
width: 200px;
height: 100px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
margin: 20px 0;
opacity: 1;
transition: opacity 0.5s ease;
}
.fade-box.fade-out {
opacity: 0;
pointer-events: none; /* ไม่สามารถ interact ได้เมื่อ fade out */
}
</style>
<script>
const box = document.getElementById("fadeBox");
const fadeBtn = document.getElementById("fadeBtn");
let isFadedOut = false;
fadeBtn.addEventListener("click", () => {
if (isFadedOut) {
box.classList.remove("fade-out");
fadeBtn.textContent = "Fade Out";
} else {
box.classList.add("fade-out");
fadeBtn.textContent = "Fade In";
}
isFadedOut = !isFadedOut;
});
</script>
3. Slide Animation - เลื่อนเข้าออก
<!-- HTML -->
<button id="slideBtn">Toggle Content</button>
<div id="slideBox" class="slide-content">
<h3>Hidden Content</h3>
<p>This content slides up and down smoothly!</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<style>
.slide-content {
background: #ecf0f1;
border-radius: 8px;
margin-top: 20px;
padding: 20px;
max-height: 300px;
overflow: hidden;
transition: all 0.5s ease;
}
.slide-content.slide-up {
max-height: 0;
padding: 0 20px;
opacity: 0;
}
</style>
<script>
const box = document.getElementById("slideBox");
const btn = document.getElementById("slideBtn");
btn.addEventListener(\"click\", () => {
box.classList.toggle(\"slide-up\");
btn.textContent = box.classList.contains(\"slide-up\") ? \"Show Content\" : \"Hide Content\";
});
</script>
4. Color Animation - เปลี่ยนสี
<!-- HTML -->
<div id=\"colorBox\" class=\"color-box\"></div>
<button id=\"colorBtn\">Animate Color</button>
<style>
.color-box {
width: 150px;
height: 150px;
background: #3498db;
border-radius: 10px;
margin: 20px 0;
transition: background-color 1s ease;
}
</style>
<script>
const colorBox = document.getElementById(\"colorBox\");
const colorBtn = document.getElementById(\"colorBtn\");
const colors = [\"#3498db\", \"#e74c3c\", \"#2ecc71\", \"#f39c12\", \"#9b59b6\"];
let colorIndex = 0;
colorBtn.addEventListener(\"click\", () => {
colorIndex = (colorIndex + 1) % colors.length;
colorBox.style.backgroundColor = colors[colorIndex];
});
</script>
- 🎨 ใช้ CSS transitions สำหรับ animations (ไม่ใช้ setInterval/setTimeout)
- ⚡ CSS animations ทำให้ browser optimize ได้ดีกว่า
- 🎯 ใช้ JavaScript เพียง toggle CSS classes
- 📱 ใช้ pointer-events: none เพื่อป้องกัน interaction ระหว่าง animation
Real-time Updates - อัปเดตทันที
Real-time Updates คือการอัปเดตข้อมูลแบบ live โดยไม่ต้อง refresh page ให้ user experience ที่ดีขึ้น เช่น live search, auto-complete, character counter:
1. Live Search - ค้นหาทันที
<!-- HTML -->
<input id=\"searchInput\" type=\"text\" placeholder=\"🔍 Search fruits...\" class=\"search-box\">
<ul id=\"results\" class=\"search-results\">
<li>🍎 Apple</li>
<li>🍌 Banana</li>
<li>🍒 Cherry</li>
<li>📅 Date</li>
<li>🫐 Elderberry</li>
<li>🍇 Fig</li>
<li>🍇 Grape</li>
</ul>
<style>
.search-box {
width: 100%;
padding: 10px;
font-size: 16px;
border: 2px solid #3498db;
border-radius: 5px;
margin-bottom: 10px;
}
.search-results {
list-style: none;
padding: 0;
max-height: 300px;
overflow-y: auto;
}
.search-results li {
padding: 10px;
background: #ecf0f1;
margin: 5px 0;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.search-results li:hover {
background: #bdc3c7;
transform: translateX(5px);
}
.search-results li.hidden {
display: none;
}
.search-results li.highlight {
background: #f39c12;
color: white;
}
</style>
<script>
const searchInput = document.getElementById(\"searchInput\");
const results = document.getElementById(\"results\");
searchInput.addEventListener(\"input\", (event) => {
const query = event.target.value.toLowerCase().trim();
document.querySelectorAll(\"#results li\").forEach(li => {
const text = li.textContent.toLowerCase();
if (text.includes(query)) {
li.classList.remove(\"hidden\");
// Highlight matching text
if (query) {
li.classList.add(\"highlight\");
} else {
li.classList.remove(\"highlight\");
}
} else {
li.classList.add(\"hidden\");
}
});
});
</script>
2. Character Counter - นับจำนวนตัวอักษร
<!-- HTML -->
<textarea id=\"textarea\" placeholder=\"Type something...\" maxlength=\"100\" class=\"textarea-input\"></textarea>
<div class=\"char-counter\">
Characters: <span id=\"count\">0</span><span class=\"max\">/100</span>
</div>
<style>
.textarea-input {
width: 100%;
height: 100px;
padding: 10px;
border: 2px solid #3498db;
border-radius: 5px;
font-family: Arial, sans-serif;
resize: vertical;
}
.char-counter {
margin-top: 5px;
font-size: 0.9em;
color: #555;
}
.char-counter span {
font-weight: bold;
color: #3498db;
}
.char-counter.warning span {
color: #f39c12;
}
.char-counter.danger span {
color: #e74c3c;
}
.char-counter .max {
color: #999;
}
</style>
<script>
const textarea = document.getElementById(\"textarea\");
const count = document.getElementById(\"count\");
const counter = document.querySelector(\".char-counter\");
const maxLength = 100;
textarea.addEventListener(\"input\", () => {
const length = textarea.value.length;
count.textContent = length;
// เปลี่ยนสี ตามจำนวน character
if (length >= maxLength) {
counter.classList.remove(\"warning\");
counter.classList.add(\"danger\");
} else if (length >= maxLength * 0.8) {
counter.classList.remove(\"danger\");
counter.classList.add(\"warning\");
} else {
counter.classList.remove(\"warning\", \"danger\");
}
});
</script>
3. Todo List - จัดการ To-do
<!-- HTML -->
<div id=\"todoApp\" class=\"todo-app\">
<h2>📋 My Todo List</h2>
<div class=\"input-group\">
<input id=\"todoInput\" type=\"text\" placeholder=\"Add a new todo...\" class=\"todo-input\">
<button id=\"addBtn\" class=\"add-btn\">➕ Add</button>
</div>
<ul id=\"todoList\" class=\"todo-list\"></ul>
<div class=\"stats\">
Total: <span id=\"totalCount\">0</span> |
Done: <span id=\"doneCount\">0</span>
</div>
</div>
<style>
.todo-app {
max-width: 500px;
margin: 20px auto;
background: #ecf0f1;
padding: 20px;
border-radius: 10px;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.todo-input {
flex: 1;
padding: 10px;
border: none;
border-radius: 5px;
font-size: 16px;
}
.add-btn {
padding: 10px 20px;
background: #3498db;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
}
.add-btn:hover {
background: #2980b9;
}
.todo-list {
list-style: none;
padding: 0;
}
.todo {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
background: white;
margin: 5px 0;
border-radius: 5px;
border-left: 4px solid #3498db;
}
.todo.completed {
opacity: 0.6;
text-decoration: line-through;
border-left-color: #2ecc71;
}
.todo-text {
flex: 1;
cursor: pointer;
}
.todo-delete {
background: #e74c3c;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 0.9em;
}
.todo-delete:hover {
background: #c0392b;
}
.todo-checkbox {
cursor: pointer;
width: 20px;
height: 20px;
}
.stats {
margin-top: 20px;
text-align: center;
color: #555;
font-size: 0.9em;
}
.stats span {
font-weight: bold;
color: #3498db;
}
</style>
<script>
const input = document.getElementById(\"todoInput\");
const addBtn = document.getElementById(\"addBtn\");
const todoList = document.getElementById(\"todoList\");
const totalCount = document.getElementById(\"totalCount\");
const doneCount = document.getElementById(\"doneCount\");
function updateStats() {
const todos = document.querySelectorAll(\".todo\");
const completed = document.querySelectorAll(\".todo.completed\").length;
totalCount.textContent = todos.length;
doneCount.textContent = completed;
}
function addTodo() {
const text = input.value.trim();
if (text === \"\") return;
const li = document.createElement(\"li\");
li.className = \"todo\";
li.innerHTML = `
<input type=\"checkbox\" class=\"todo-checkbox\">
<span class=\"todo-text\">${text}</span>
<button class=\"todo-delete\">🗑️ Delete</button>
`;
// Mark as complete
li.querySelector(\".todo-checkbox\").addEventListener(\"change\", () => {
li.classList.toggle(\"completed\");
updateStats();
});
// Delete
li.querySelector(\".todo-delete\").addEventListener(\"click\", () => {
li.remove();
updateStats();
});
// Click text to toggle complete
li.querySelector(\".todo-text\").addEventListener(\"click\", () => {
li.querySelector(\".todo-checkbox\").click();
});
todoList.appendChild(li);
input.value = \"\";
updateStats();
}
// Add event listeners
addBtn.addEventListener(\"click\", addTodo);
// Enter key
input.addEventListener(\"keypress\", (event) => {
if (event.key === \"Enter\") {
addTodo();
}
});
updateStats(); // Initialize
</script>
Best Practices - วิธีที่ถูกต้อง
Best Practices ช่วยให้โค้ด maintainable, efficient, และ secure มากขึ้น:
1. ใช้ External JavaScript ✅
แยก JavaScript ออกไปในไฟล์ .js เพื่อเก็บโค้ดให้สะอาด:
<body>
<h1>Title</h1>
<script>
// โค้ด JavaScript ยาว ๆ ที่นี่
</script>
</body>
<body>
<h1>Title</h1>
<script src="app.js" defer></script>
</body>
2. ใช้ querySelector แทน getElementById
querySelector ใช้ได้ทั่วไป ใช้ CSS selector ที่คุ้นเคย:
const el = document.getElementById("myId");
const els = document.getElementsByClassName("myClass");
const tags = document.getElementsByTagName("p");
const el = document.querySelector("#myId");
const els = document.querySelectorAll(".myClass");
const tags = document.querySelectorAll("p");
const nested = document.querySelector("#header h1");
3. Event Delegation สำหรับ Dynamic Elements
เมื่อต้องจัดการ elements ที่สร้างภายหลัง ให้ใช้ event delegation:
// ทุก li ที่มีตอนนี้จะมี listener
document.querySelectorAll("li").forEach(li => {
li.addEventListener("click", handler);
});
// li ใหม่ที่สร้างภายหลังจะไม่มี listener!
// เพิ่ม listener ที่ parent element
document.getElementById("list").addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log("Item clicked:", e.target.textContent);
}
});
// li ใหม่ได้ listener อัตโนมัติ!
4. ป้องกัน XSS - Sanitize User Input
⚠️ สำคัญ: ใช้ textContent แทน innerHTML:
const userInput = "<img src=x onerror=alert('Hacked')>";
element.innerHTML = userInput;
// ❌ JavaScript จะรัน!
const userInput = "<img src=x onerror=alert('Hacked')>";
element.textContent = userInput;
// ✅ แสดงเป็น text ธรรมชาติ
5. ใช้ const โดยค่าเริ่มต้น
ลำดับ: const > let > var (ไม่ใช้ var เลย):
// ❌ var มี scope issues
var button = document.getElementById("myBtn");
// ✅ const ไม่เปลี่ยนแปลง
const button = document.getElementById("myBtn");
const config = { timeout: 5000 };
// let เมื่อต้องเปลี่ยนค่า
let count = 0;
count++;
6. ตรวจสอบ Element ก่อนใช้
ป้องกัน error เมื่อ element ไม่มี:
const button = document.getElementById("myBtn");
button.addEventListener("click", handler);
// ❌ Error: Cannot read property 'addEventListener' of null
const button = document.getElementById("myBtn");
if (button) {
button.addEventListener("click", handler);
}
7. Debounce สำหรับ Heavy Operations
Debounce ช่วยลด number of function calls:
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
// ✅ Debounce resize event
const handleResize = debounce(() => {
console.log("Window resized");
}, 500);
window.addEventListener("resize", handleResize);
8. ใช้ Data Attributes สำหรับ Custom Data
ใช้ data-* attributes เพื่อเก็บ custom data:
<!-- HTML -->
<button data-action="delete" data-id="123">Delete</button>
<script>
document.addEventListener("click", (e) => {
if (e.target.hasAttribute("data-action")) {
const action = e.target.dataset.action;
const id = e.target.dataset.id;
console.log(`Action: ${action}, ID: ${id}`);
}
});
</script>
9. Error Handling with try/catch
จัดการ error เพื่อให้ app ไม่ crash:
// ❌ ไม่ดี - ไม่มี error handling
const data = JSON.parse(userInput);
// ✅ ดี - มี error handling
try {
const data = JSON.parse(userInput);
console.log("Parsed:", data);
} catch (error) {
console.error("Invalid JSON:", error.message);
}
10. ใช้ Template Literals
ใช้ backticks แทน string concatenation:
// ❌ ยากอ่าน
const html = "<div class='item'>" + name + "</div>";
// ✅ สะอาด
const html = `
<div class="item">${name}</div>
`;
- 📁 External JavaScript (แยก .js file)
- 🔍 querySelector (CSS selectors)
- 📌 Event Delegation (dynamic elements)
- 🔒 ป้องกัน XSS (textContent, not innerHTML)
- 📦 const by default, let when needed
- ✔️ ตรวจสอบ element ก่อนใช้
- ⚡ Debounce heavy operations
- 🏷️ Data attributes สำหรับ custom data
- ❌ try/catch error handling
- 📝 Template literals
📚 สรุป
JavaScript ทำให้ HTML กลายเป็น web application ที่มีชีวิตและตัวอบสนอง ด้วย:
- DOM Manipulation - แก้ไข HTML แบบ real-time
- Event Handling - ตอบสนองต่อการกระทำของผู้ใช้
- Form Validation - ตรวจสอบข้อมูลก่อนส่ง
- Animation & Effects - ส่วนสำคัญของ UX
- Real-time Updates - ทำให้เพจตรวจสอบข้อมูลปัจจุบัน
- Best Practices - เขียนโค้ดที่สะอาดและปลอดภัย