Skip to content

Study Guide: JavaScript + DOM + State

JavaScript คือภาษาที่ทำให้เว็บ มีชีวิต — กดปุ่มแล้วเกิดอะไรบางอย่าง, กรอกฟอร์มแล้ว validate, โหลดข้อมูลมาแสดง

script.js LIVE DEMO
> const name = "Alex";
> let score = 0; score += 10;
> const fruits = ["apple", "banana"];
> typeof 42; typeof "hello"; typeof true;

กด Run เพื่อรันโค้ดแต่ละบรรทัด -- ผลลัพธ์จะแสดงเหมือน browser console

Mini Web Page

Hello World

This is a paragraph element.

Click to run:

กดแต่ละปุ่มเพื่อเปลี่ยน DOM แบบ real-time -- สังเกตว่า JavaScript เข้าถึง HTML ได้โดยตรง

click
Clicked: 0 times
input
Characters: 0 / 50
mousemove
Move your mouse here
Position: (-, -)

แต่ละ card ใช้ event ต่างกัน -- นี่คือวิธีที่ JavaScript ฟังการกระทำของ user

State → Render Pattern
0
State Log:
state: 0 (initial)

กดปุ่มแล้วดู State Log -- ทุกครั้งที่ state เปลี่ยน UI จะ render ใหม่อัตโนมัติ

Variables — กล่องเก็บข้อมูล

Section titled “Variables — กล่องเก็บข้อมูล”
// const = ค่าไม่เปลี่ยน (ใช้เป็น default)
const name = "Alex";
const age = 25;
// let = ค่าเปลี่ยนได้
let score = 0;
score = score + 10; // score = 10
// ห้ามใช้ var — มันเป็น legacy แล้ว
Output
> const name = "Alex"; // "Alex"
> let score = 0; score += 10; // 10
> typeof true // "boolean"

Naming Conventions — การตั้งชื่อตัวแปร

Section titled “Naming Conventions — การตั้งชื่อตัวแปร”

การตั้งชื่อตัวแปรมีหลาย style — แต่ละทีมจะเลือกใช้แบบที่ตกลงกัน สิ่งสำคัญคือ ใช้ให้สม่ำเสมอ ตลอดทั้งโปรเจกต์

Styleตัวอย่างใช้ที่ไหน
camelCaseuserName, isLoggedIn, getTotalPriceJavaScript, TypeScript, Java — ใช้บ่อยที่สุดในโลก frontend
snake_caseuser_name, is_logged_in, get_total_pricePython, Ruby, Rust, ชื่อไฟล์, database columns
PascalCaseUserName, ShoppingCart, TodoItemชื่อ Class, React Component, Type/Interface
kebab-caseuser-name, main-content, nav-barCSS class, HTML attributes, URL slugs, ชื่อไฟล์
SCREAMING_SNAKE_CASEMAX_RETRY, API_BASE_URL, DEFAULT_TIMEOUTค่าคงที่ (constants) ที่ไม่เปลี่ยนแปลง
naming-examples.js
// camelCase — ตัวแปรและฟังก์ชันใน JavaScript
const firstName = "Alex";
const isActive = true;
const getUserProfile = () => { /* ... */ };
// PascalCase — Component และ Class
class ShoppingCart { /* ... */ }
function TodoItem({ text }) { /* ... */ }
// SCREAMING_SNAKE_CASE — ค่าคงที่
const MAX_ITEMS = 100;
const API_BASE_URL = "https://api.example.com";
// snake_case — ข้อมูลจาก API มักมาแบบนี้
const apiResponse = {
user_name: "alex", // ← จาก server (snake_case)
created_at: "2026-01-01",
};
// แปลงเป็น camelCase ตอนใช้ใน frontend
const userName = apiResponse.user_name;
// String — ข้อความ
const greeting = "สวัสดี";
// Number — ตัวเลข
const price = 299.50;
// Boolean — จริง/เท็จ
const isLoggedIn = true;
// Array — รายการ
const fruits = ["apple", "banana", "mango"];
console.log(fruits[0]); // "apple"
console.log(fruits.length); // 3
// Object — ข้อมูลแบบ key-value
const user = {
name: "Alex",
age: 25,
role: "designer",
};
console.log(user.name); // "Alex"
Output
String "สวัสดี"
Number 42
Boolean true
Array [1, 2, 3]
Object {name:"Alex"}

Functions — ก้อนโค้ดที่เรียกใช้ซ้ำได้

Section titled “Functions — ก้อนโค้ดที่เรียกใช้ซ้ำได้”
// Arrow function (แนะนำ)
const greet = (name) => {
return `Hello, ${name}!`;
};
// เรียกใช้
console.log(greet("Alex")); // "Hello, Alex!"
// แบบสั้น — ถ้ามีแค่ return บรรทัดเดียว
const double = (n) => n * 2;
console.log(double(5)); // 10

Array Methods ที่ใช้บ่อยมาก

Section titled “Array Methods ที่ใช้บ่อยมาก”
const numbers = [1, 2, 3, 4, 5];
// .map() — แปลงทุกตัว
const doubled = numbers.map((n) => n * 2);
// [2, 4, 6, 8, 10]
// .filter() — กรองเอาเฉพาะที่ผ่านเงื่อนไข
const evens = numbers.filter((n) => n % 2 === 0);
// [2, 4]
// .find() — หาตัวแรกที่ตรงเงื่อนไข
const found = numbers.find((n) => n > 3);
// 4
// .forEach() — วนลูปทำอะไรบางอย่าง
numbers.forEach((n) => console.log(n));
Output
input [1, 2, 3, 4, 5]
result Click a method above

DOM — สะพานเชื่อม JavaScript กับ HTML

Section titled “DOM — สะพานเชื่อม JavaScript กับ HTML”

DOM (Document Object Model) คือ “ต้นไม้” ของ HTML ที่ JavaScript เข้าถึงได้

document html head body h1 div p button
// เลือก 1 ตัว — ใช้ CSS selector
const title = document.querySelector("h1");
const btn = document.querySelector("#submit-btn");
const card = document.querySelector(".card");
// เลือกหลายตัว — ได้ NodeList กลับมา
const items = document.querySelectorAll(".item");
items.forEach((item) => {
console.log(item.textContent);
});
const title = document.querySelector("h1");
// เปลี่ยนข้อความ
title.textContent = "Hello World!";
// เปลี่ยน style
title.style.color = "blue";
title.style.fontSize = "2rem";
// เพิ่ม/ลบ class
title.classList.add("active");
title.classList.remove("hidden");
title.classList.toggle("dark-mode"); // เพิ่มถ้าไม่มี, ลบถ้ามี
// สร้าง element
const li = document.createElement("li");
li.textContent = "New Item";
li.classList.add("todo-item");
// เพิ่มเข้าไปในหน้า
const list = document.querySelector("#todo-list");
list.appendChild(li);
// ลบ element
li.remove();
Output

Hello

World

Events — ฟังการกระทำของ user

Section titled “Events — ฟังการกระทำของ user”
const btn = document.querySelector("#my-btn");
// คลิก
btn.addEventListener("click", () => {
console.log("Button clicked!");
});
// พิมพ์ใน input
const input = document.querySelector("#search");
input.addEventListener("input", (e) => {
console.log(e.target.value); // ค่าที่พิมพ์
});
// ส่งฟอร์ม
const form = document.querySelector("#my-form");
form.addEventListener("submit", (e) => {
e.preventDefault(); // ป้องกันหน้ารีเฟรช!
const data = new FormData(form);
console.log(data.get("name"));
});
Output
click
input 0 chars
mousemove
(0, 0)

State — หัวใจของ Frontend

Section titled “State — หัวใจของ Frontend”

State = ข้อมูลที่เปลี่ยนแปลงได้ และเมื่อเปลี่ยนแล้ว UI ต้องอัปเดตตาม

แนวคิด: State → Render → Event → State…

Section titled “แนวคิด: State → Render → Event → State…”
User กดปุ่ม Event Handler อัปเดต State เรียก render() UI อัปเดต
// 1. State
let count = 0;
// 2. Render function — อ่าน state แล้วอัปเดต UI
const render = () => {
document.querySelector("#count").textContent = count;
};
// 3. Event handlers — เปลี่ยน state แล้ว render ใหม่
document.querySelector("#plus").addEventListener("click", () => {
count++; // เปลี่ยน state
render(); // อัปเดต UI
});
document.querySelector("#minus").addEventListener("click", () => {
count--;
render();
});
// เรียก render ครั้งแรก
render();
Output
0
State Log
0
State Render Event State

ตัวอย่าง: Todo List พร้อม State

Section titled “ตัวอย่าง: Todo List พร้อม State”
// === State ===
let todos = [];
let nextId = 1;
// === Render ===
const render = () => {
const list = document.querySelector("#todo-list");
list.innerHTML = ""; // เคลียร์ก่อน
todos.forEach((todo) => {
const li = document.createElement("li");
li.className = `todo-item ${todo.completed ? "completed" : ""}`;
li.innerHTML = `
<input type="checkbox" ${todo.completed ? "checked" : ""}>
<span>${todo.text}</span>
<button class="delete-btn">x</button>
`;
// Toggle completed
li.querySelector("input").addEventListener("change", () => {
todo.completed = !todo.completed;
render();
});
// Delete
li.querySelector(".delete-btn").addEventListener("click", () => {
todos = todos.filter((t) => t.id !== todo.id);
render();
});
list.appendChild(li);
});
// อัปเดตจำนวนที่เหลือ
const remaining = todos.filter((t) => !t.completed).length;
document.querySelector("#count").textContent = `${remaining} remaining`;
};
// === Event: Add todo ===
document.querySelector("#todo-form").addEventListener("submit", (e) => {
e.preventDefault();
const input = document.querySelector("#todo-input");
const text = input.value.trim();
if (!text) return;
todos.push({ id: nextId++, text, completed: false });
input.value = "";
render();
});
render();

localStorage — บันทึกข้อมูลบน Browser

Section titled “localStorage — บันทึกข้อมูลบน Browser”

localStorage เก็บข้อมูลไว้แม้ปิดหน้าเว็บ — เหมาะสำหรับ settings, todos, theme

// บันทึก (ต้องแปลงเป็น string ก่อน)
localStorage.setItem("todos", JSON.stringify(todos));
localStorage.setItem("theme", "dark");
// อ่าน (ต้องแปลงกลับเป็น object)
const saved = localStorage.getItem("todos");
if (saved) {
todos = JSON.parse(saved);
}
const theme = localStorage.getItem("theme") || "light";
// โหลด state จาก localStorage ตอนเริ่มต้น
let todos = JSON.parse(localStorage.getItem("todos") || "[]");
// บันทึกทุกครั้งที่ state เปลี่ยน
const save = () => {
localStorage.setItem("todos", JSON.stringify(todos));
};
// ใน event handler — เปลี่ยน state + save + render
document.querySelector("#todo-form").addEventListener("submit", (e) => {
e.preventDefault();
todos.push({ id: Date.now(), text: "New todo", completed: false });
save(); // บันทึก
render(); // อัปเดต UI
});
Output — localStorage
Write to localStorage
Saved!
localStorage.setItem("myData", "...")
Browser localStorage
Key Value
myData (empty)
localStorage.getItem("myData") → "..."
localStorage persists even after page reload — try refreshing!

// อ่าน theme จาก localStorage
let isDark = localStorage.getItem("theme") === "dark";
const applyTheme = () => {
document.body.classList.toggle("dark", isDark);
localStorage.setItem("theme", isDark ? "dark" : "light");
};
document.querySelector("#theme-toggle").addEventListener("click", () => {
isDark = !isDark;
applyTheme();
});
// ใช้ theme ตอนโหลดหน้า
applyTheme();
CSS Variables สำหรับ theme
/* CSS Variables สำหรับ theme */
body {
--bg: #ffffff;
--text: #333333;
background: var(--bg);
color: var(--text);
transition: background 0.3s, color 0.3s;
}
body.dark {
--bg: #1a1a2e;
--text: #e4e4e7;
}

การ debug คือทักษะที่สำคัญมาก — เขียนโค้ดเก่งแค่ไหนก็ต้องเจอ bug เสมอ JavaScript มีเครื่องมือ debug ใน console ที่ใช้ง่ายมาก

console methods ที่ต้องรู้

Section titled “console methods ที่ต้องรู้”
Methodใช้ทำอะไร
console.log()แสดงค่าตัวแปรทั่วไป
console.table()แสดง array/object เป็นตาราง อ่านง่าย
console.error()แสดง error สีแดง เห็นชัด
console.warn()แสดง warning สีเหลือง
console.group() / console.groupEnd()จัดกลุ่ม log ให้เป็นระเบียบ
// console.log() — ใช้บ่อยที่สุด
const user = { name: "Alex", role: "designer" };
console.log("User:", user);
console.log("Name is:", user.name);
// console.table() — สวยงาม อ่านง่าย
const todos = [
{ id: 1, text: "Learn JS", done: true },
{ id: 2, text: "Build app", done: false },
];
console.table(todos);
// console.error() — ใช้ตอนอะไรผิดพลาด
console.error("Something went wrong!", { code: 404 });
// console.group() — จัดกลุ่ม log
console.group("User Info");
console.log("Name:", user.name);
console.log("Role:", user.role);
console.groupEnd();

error ที่เจอบ่อยที่สุดมี 2 แบบ:

// TypeError — เรียก method บนค่าที่ไม่มี
let data = undefined;
data.name; // TypeError: Cannot read properties of undefined
// ReferenceError — ใช้ตัวแปรที่ยังไม่ได้สร้าง
console.log(myVariable); // ReferenceError: myVariable is not defined

เมื่อเขียนโค้ดจริง ต้องคิดเสมอว่า “ถ้ามันพังจะเกิดอะไรขึ้น?” — try/catch ช่วยให้จัดการ error ได้อย่างสง่างาม แทนที่จะให้ app พังทั้งหมด

try {
// โค้ดที่อาจจะพัง
const data = JSON.parse("this is not json");
} catch (error) {
// จัดการเมื่อพัง
console.error("Parse failed:", error.message);
}
// โปรแกรมยังทำงานต่อได้ ไม่พังทั้งหมด

Common Errors และวิธีป้องกัน

Section titled “Common Errors และวิธีป้องกัน”
// TypeError: Cannot read properties of undefined
const user = undefined;
console.log(user.name); // พัง!
// ตรวจสอบก่อนใช้ (Defensive Coding)
const user2 = undefined;
console.log(user2?.name); // undefined — ไม่พัง (optional chaining)

ตัวอย่าง: Error Handling กับ localStorage

Section titled “ตัวอย่าง: Error Handling กับ localStorage”
const loadTodos = () => {
try {
const saved = localStorage.getItem("todos");
return saved ? JSON.parse(saved) : [];
} catch (error) {
console.error("Failed to load todos:", error.message);
return []; // ถ้า parse ไม่ได้ ให้ใช้ array ว่าง
}
};

JavaScript เป็นภาษา single-threaded — ทำได้ทีละอย่าง แต่การโหลดข้อมูลจาก server ใช้เวลา ถ้ารอจนเสร็จ UI จะค้าง ดังนั้นจึงต้องใช้ async

setTimeout — ทำอะไรหลังจากรอ

Section titled “setTimeout — ทำอะไรหลังจากรอ”
console.log("Start");
setTimeout(() => {
console.log("After 2 seconds");
}, 2000);
console.log("End");
// ลำดับ output: "Start" → "End" → "After 2 seconds"

fetch() — โหลดข้อมูลจาก API

Section titled “fetch() — โหลดข้อมูลจาก API”
// แบบ .then() / .catch()
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((user) => {
console.log(user.name); // "Leanne Graham"
})
.catch((error) => {
console.error("Failed to fetch:", error.message);
});

async/await — เขียนอ่านง่ายกว่า

Section titled “async/await — เขียนอ่านง่ายกว่า”
const loadUser = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users/1"
);
const user = await response.json();
console.log(user.name); // "Leanne Graham"
// อัปเดต UI
document.querySelector("#user-name").textContent = user.name;
document.querySelector("#user-email").textContent = user.email;
} catch (error) {
console.error("Failed to load user:", error.message);
document.querySelector("#user-name").textContent = "Error loading data";
}
};
loadUser();

ตัวอย่างจริง: โหลดรายการ posts จาก API

Section titled “ตัวอย่างจริง: โหลดรายการ posts จาก API”
// === State ===
let posts = [];
// === Render ===
const render = () => {
const list = document.querySelector("#post-list");
list.innerHTML = posts
.map(
(post) => `
<li class="post-item">
<h3>${post.title}</h3>
<p>${post.body}</p>
</li>
`
)
.join("");
};
// === Load Data ===
const loadPosts = async () => {
try {
document.querySelector("#status").textContent = "Loading...";
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts?_limit=5"
);
posts = await response.json();
render();
document.querySelector("#status").textContent = "";
} catch (error) {
document.querySelector("#status").textContent = "Failed to load posts";
}
};
loadPosts();

เขียนโค้ดให้ “ทำงานได้” ไม่พอ — ต้อง อ่านง่าย ด้วย เพราะคนที่อ่านโค้ดเราบ่อยที่สุดคือ “ตัวเราในอนาคต”

ตั้งชื่อตัวแปรให้สื่อความหมาย

Section titled “ตั้งชื่อตัวแปรให้สื่อความหมาย”
// ชื่อไม่บอกอะไรเลย
const d = new Date();
const arr = ["apple", "banana"];
const flag = true;
// ใช้ const ก่อน เปลี่ยนเป็น let เมื่อจำเป็น
const name = "Alex";
const items = [1, 2, 3]; // push/pop ได้ ไม่ต้อง let
let count = 0; // ต้องเปลี่ยนค่า ใช้ let

จัดโครงสร้างไฟล์

Section titled “จัดโครงสร้างไฟล์”
script.js — โครงสร้างที่แนะนำ
// === 1. State (ด้านบนสุด) ===
let todos = [];
let filter = "all";
// === 2. Render Functions ===
const render = () => {
// อัปเดต UI ตาม state
};
const renderCount = () => {
// อัปเดตจำนวน
};
// === 3. Event Handlers (ด้านล่าง) ===
document.querySelector("#form").addEventListener("submit", (e) => {
e.preventDefault();
// เปลี่ยน state → render
});
// === 4. Initialize ===
render();

เขียน comment เมื่อจำเป็น

Section titled “เขียน comment เมื่อจำเป็น”
// Bad — comment ทุกบรรทัด ซ้ำกับโค้ด
// เพิ่ม 1 เข้า count
count = count + 1;
// เรียก render
render();
// Good — comment เฉพาะตอนอธิบาย "ทำไม"
// ใช้ Date.now() แทน auto-increment เพราะต้อง unique ข้าม sessions
const id = Date.now();
// Don't: ใช้ innerHTML กับ user input ตรงๆ (XSS risk)
li.innerHTML = userInput;
// Do: ใช้ textContent สำหรับ text ธรรมดา
li.textContent = userInput;

Node.js & npm — เตรียมพร้อมสำหรับ React & Tailwind

Section titled “Node.js & npm — เตรียมพร้อมสำหรับ React & Tailwind”

ตั้งแต่ Chapter 3 เป็นต้นไป เราจะใช้เครื่องมือที่ต้อง Node.js — ดังนั้นมาติดตั้งและเข้าใจมันก่อน

Node.js คือ JavaScript runtime — ทำให้รัน JavaScript นอก browser ได้ เครื่องมือสมัยใหม่อย่าง Vite, Tailwind, React ล้วนต้องใช้ Node.js

Browser JSทำงานใน browserจัดการ DOM, UI Node.jsทำงานบนเครื่องคุณรัน build tools, servers
  1. ไปที่ nodejs.org

  2. ดาวน์โหลดเวอร์ชัน LTS (Long Term Support) — เสถียรที่สุด

  3. ติดตั้งตาม OS แล้วเปิด Terminal ตรวจสอบ:

    Terminal window
    node --version # ควรได้ v22.x.x หรือสูงกว่า
    npm --version # ควรได้ 10.x.x หรือสูงกว่า

npm (Node Package Manager) คือ “App Store ของโค้ด” — มี library ให้โหลดใช้ฟรีกว่า 2 ล้านตัว ทุกครั้งที่เราใช้ React, Tailwind, หรือ Framer Motion เราติดตั้งผ่าน npm

Terminal
# ติดตั้ง package
npm install react # ติดตั้ง 1 ตัว
npm install react react-dom # ติดตั้งหลายตัวพร้อมกัน
# ติดตั้งแบบ dev dependency (ใช้ตอนพัฒนาเท่านั้น)
npm install -D tailwindcss
# ลบ package
npm uninstall react
# ดู packages ที่ติดตั้งแล้ว
npm list

package.json — สูตรอาหารของโปรเจกต์

Section titled “package.json — สูตรอาหารของโปรเจกต์”

ทุกโปรเจกต์ที่ใช้ npm จะมีไฟล์ package.json — เก็บข้อมูลโปรเจกต์ + รายชื่อ packages ที่ใช้

package.json
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"tailwindcss": "^4.0.0",
"vite": "^6.0.0"
}
}
Sectionคืออะไร
scriptsคำสั่งลัด — รัน npm run dev แทนการพิมพ์คำสั่งยาว
dependenciespackages ที่ใช้ตอนรัน (React, etc.)
devDependenciespackages ที่ใช้ตอนพัฒนาเท่านั้น (Vite, Tailwind, etc.)
Terminal
# สร้างโปรเจกต์ใหม่
npm init -y # สร้าง package.json
# ติดตั้ง dependencies
npm install # อ่าน package.json แล้วโหลด packages ทั้งหมด
ไฟล์/โฟลเดอร์คืออะไรต้อง commit ไหม?
package.jsonรายชื่อ packagesต้อง commit
package-lock.jsonเวอร์ชันที่แน่นอนของ packagesต้อง commit
node_modules/โค้ดจริงของ packages ที่โหลดมาห้าม commit — ใส่ใน .gitignore

npx — รัน package โดยไม่ต้องติดตั้ง

Section titled “npx — รัน package โดยไม่ต้องติดตั้ง”
Terminal
# สร้างโปรเจกต์ React ด้วย Vite (ไม่ต้อง install Vite ก่อน)
npx create-vite@latest my-app
# รัน package ชั่วคราว
npx tailwindcss init

npm ไม่ใช่ตัวเดียว — มีตัวอื่นที่เร็วกว่า ถ้าเจอในโปรเจกต์ของทีม ก็ใช้วิธีเดียวกัน

Package Managerคำสั่งติดตั้งจุดเด่น
npmมาพร้อม Node.jsมาตรฐาน, ใช้ง่าย
pnpmnpm install -g pnpmเร็ว, ประหยัดเนื้อที่
yarnnpm install -g yarnเสถียร, ใช้กันมานาน
bunbun.shเร็วมาก, runtime + package manager ในตัว
Terminal
# ทุกตัวใช้คำสั่งคล้ายกัน
npm install react # npm
pnpm add react # pnpm
yarn add react # yarn
bun add react # bun

สรุป: คำสั่ง npm ที่ใช้บ่อย

Section titled “สรุป: คำสั่ง npm ที่ใช้บ่อย”
คำสั่งทำอะไร
npm init -yสร้าง package.json ใหม่
npm installติดตั้ง packages ทั้งหมดจาก package.json
npm install <pkg>ติดตั้ง package เพิ่ม
npm install -D <pkg>ติดตั้งเป็น dev dependency
npm uninstall <pkg>ลบ package
npm run devรัน script “dev” (เปิด dev server)
npm run buildรัน script “build” (สร้างไฟล์ production)
npx <cmd>รันคำสั่งจาก package โดยไม่ต้องติดตั้ง

  1. Counter App — สร้างปุ่ม +/- ที่เปลี่ยนตัวเลข พร้อมปุ่ม Reset

  2. Todo List — เพิ่ม, ลบ, toggle done ใช้ state pattern

  3. Dark Mode — toggle theme แล้วบันทึกใน localStorage

  4. รวมทั้งหมด — Todo List + Dark Mode + localStorage เป็นโปรเจกต์เดียว