คู่มือขั้นตอนทีละขั้นตอนชัดเจนในการสร้างแอป to-do ขนาดเล็ก: วางแผนฟีเจอร์ สร้างหน้าจอ เพิ่มตรรกะ บันทึกข้อมูล ทดสอบ และเผยแพร่

เมื่อผมใช้คำว่า “แอป” ในคู่มือนี้ ผมหมายถึง web app เล็กๆ: หน้าเดียวที่คุณเปิดใน เบราว์เซอร์ ที่ตอบสนองต่อการคลิกและการพิมพ์ของคุณ ไม่มีการติดตั้ง ไม่มีบัญชี ไม่มีการตั้งค่าหนัก—เป็นโปรเจกต์เรียบง่ายที่รันได้ในเครื่อง
เมื่อจบ คุณจะมีแอป to‑do ที่สามารถ:
localStorage (เพื่อไม่ให้ข้อมูลหายเมื่อปิดแท็บ)มันจะไม่สมบูรณ์แบบหรือระดับองค์กร และนั่นแหละคือจุดประสงค์ นี่เป็นโปรเจกต์สำหรับผู้เริ่มต้นที่ออกแบบมาเพื่อสอนพื้นฐานโดยไม่ใส่เครื่องมือเยอะเกินความจำเป็น
คุณจะสร้างแอปทีละขั้นตอนและเรียนรู้ส่วนหลักของการทำงานของ front-end web apps:
อย่าซับซ้อนเกินไป คุณต้องการแค่:
ถ้าคุณสามารถสร้างโฟลเดอร์และแก้ไขไฟล์ได้สองสามไฟล์ คุณก็พร้อมแล้ว
ก่อนจะเขียนโค้ด ให้กำหนดก่อนว่า “ความสำเร็จ” เป็นอย่างไร คู่มือนี้สร้างแอปเล็กๆ หนึ่งตัวที่มีหน้าที่ชัดเจน: ช่วยติดตามงานที่คุณต้องการทำ
เขียนเป้าหมายสั้นๆ หนึ่งประโยคไว้ตรงหน้าคุณในขณะสร้าง:
“แอปนี้ให้ฉันเพิ่มงานลงในรายการเพื่อไม่ให้ฉันลืมมัน”
แค่นั้นแหละ หากคุณอยากเพิ่มปฏิทิน การเตือน ป้ายกำกับ หรือบัญชีผู้ใช้ ให้เก็บไอเดียเหล่านั้นไว้ภายหลัง
ทำสองรายการสั้นๆ:
สิ่งที่ต้องมี (สำหรับโปรเจกต์นี้):
สิ่งที่เสริมได้ (ไม่จำเป็นวันนี้): วันครบกำหนด, ลำดับความสำคัญ, หมวดหมู่, ค้นหา, ลากแล้ววาง, ซิงก์บนคลาวด์
การคง “สิ่งที่ต้องมี” ให้เล็กช่วยให้คุณเสร็จจริง
แอปนี้อาจเป็นหน้าเดียวที่มี:
ชัดเจนไว้จะได้ไม่ติดขัด:
เมื่อกำหนดแล้ว คุณก็พร้อมที่จะตั้งค่าไฟล์โปรเจกต์
ก่อนเขียนโค้ด เรามาสร้างที่อยู่เล็กๆ สำหรับแอป การจัดระเบียบไฟล์ตั้งแต่ต้นจะช่วยให้ขั้นตอนถัดไปราบรื่น
สร้างโฟลเดอร์ใหม่บนคอมพิวเตอร์และตั้งชื่อเช่น todo-app โฟลเดอร์นี้จะเก็บทุกอย่างของโปรเจกต์นี้
ภายในโฟลเดอร์นั้น สร้าง สามไฟล์:
index.html (โครงหน้า)styles.css (รูปลักษณ์และการจัดวาง)app.js (พฤติกรรมและการโต้ตอบ)ถ้าคอมพิวเตอร์ของคุณซ่อนนามสกุลไฟล์ (.html) ให้แน่ใจว่าคุณสร้างไฟล์จริง ความผิดพลาดสำหรับผู้เริ่มต้นคือได้ index.html.txt
เปิดโฟลเดอร์ todo-app ในตัวแก้ไขโค้ดของคุณ (VS Code, Sublime Text ฯลฯ) แล้วเปิด index.html ในเบราว์เซอร์ของคุณ
ตอนนี้หน้าอาจว่าง และนั่นไม่เป็นไร เราจะเพิ่มเนื้อหาในขั้นตอนถัดไป
เมื่อคุณแก้ไขไฟล์ เบราว์เซอร์จะไม่อัปเดตอัตโนมัติ (เว้นแต่คุณใช้เครื่องมือที่ทำแบบนั้น)
ลูปพื้นฐานคือ:
ถ้าอะไร "ไม่ทำงาน" ให้ลองรีเฟรชก่อนเป็นอย่างแรก
คุณสามารถสร้างแอปนี้ได้โดยดับเบิลคลิก index.html แต่เซิร์ฟเวอร์ท้องถิ่นช่วยป้องกันปัญหาแปลกๆ ในภายหลัง (โดยเฉพาะเมื่อเริ่มบันทึกข้อมูลหรือโหลดไฟล์)
ตัวเลือกที่เป็นมิตรกับผู้เริ่มต้น:
python -m http.server
แล้วเปิดที่อยู่ที่มันพิมพ์ออกมา (มักจะเป็น http://localhost:8000) ในเบราว์เซอร์ของคุณ
ตอนนี้เราจะสร้างโครงที่ชัดเจนสำหรับแอป HTML นี้ HTML นี้ยังไม่ทำให้เกิดการโต้ตอบ (นั่นไว้ในขั้นต่อไป) แต่จะให้จุดที่ชัดเจนให้ JavaScript อ่านและเขียน
เราจะใส่:
ตั้งชื่อตัวแปรให้เรียบง่ายและอ่านง่าย ID/class ที่ดีจะทำให้ JavaScript ของคุณหาองค์ประกอบได้โดยไม่มีความสับสน
index.html ของคุณ\u003c!doctype html\u003e
\u003chtml lang=\"en\"\u003e
\u003chead\u003e
\u003cmeta charset=\"utf-8\" /\u003e
\u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e
\u003ctitle\u003eTo‑Do App\u003c/title\u003e
\u003clink rel=\"stylesheet\" href=\"styles.css\" /\u003e
\u003c/head\u003e
\u003cbody\u003e
\u003cmain class=\"app\" id=\"app\"\u003e
\u003ch1 class=\"app__title\" id=\"appTitle\"\u003eMy To‑Do List\u003c/h1\u003e
\u003cform class=\"task-form\" id=\"taskForm\"\u003e
\u003clabel class=\"task-form__label\" for=\"taskInput\"\u003eNew task\u003c/label\u003e
\u003cdiv class=\"task-form__row\"\u003e
\u003cinput
id=\"taskInput\"
class=\"task-form__input\"
type=\"text\"
placeholder=\"e.g., Buy milk\"
autocomplete=\"off\"
/\u003e
\u003cbutton id=\"addButton\" class=\"task-form__button\" type=\"submit\"\u003e
Add
\u003c/button\u003e
\u003c/div\u003e
\u003c/form\u003e
\u003cul class=\"task-list\" id=\"taskList\" aria-label=\"Task list\"\u003e\u003c/ul\u003e
\u003c/main\u003e
\u003cscript src=\"app.js\"\u003e\u003c/script\u003e
\u003c/body\u003e
\u003c/html\u003e
แค่นี้สำหรับโครง สังเกตว่าเราใช้ id="taskInput" และ id="taskList"—สององค์ประกอบนี้คือสิ่งที่คุณจะเรียกใช้บ่อยใน JavaScript
ตอนนี้หน้าของคุณมีอยู่แล้ว แต่ดูเหมือนเอกสารธรรมดา CSS เล็กน้อยจะช่วยให้อ่านง่ายขึ้น: ระยะขอบที่ชัดเจน ตัวอักษรอ่านง่าย และปุ่มที่รู้สึกว่าคลิกได้
คอนเทนเนอร์กึ่งกลางช่วยให้แอปโฟกัสและไม่ยืดต across หน้าจอ
/* Basic page setup */
body {
font-family: Arial, sans-serif;
background: #f6f7fb;
margin: 0;
padding: 24px;
}
/* Centered app container */
.container {
max-width: 520px;
margin: 0 auto;
background: #ffffff;
padding: 16px;
border-radius: 10px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}
แต่ละงานควรดูเป็นแถวแยกกัน มีระยะห่างสบายตา
ul { list-style: none; padding: 0; margin: 16px 0 0; }
li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border: 1px solid #e7e7ee;
border-radius: 8px;
margin-bottom: 10px;
}
.task-text { flex: 1; }
เมื่องานเสร็จ มันควรเปลี่ยนลักษณะให้มองเห็นได้ทันที
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
ให้ปุ่มมีขนาดและสไตล์เดียวกันเพื่อให้รู้สึกเป็นแอปเดียวกัน
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
พอนี้ก็พอสำหรับ UI ที่สะอาดและเป็นมิตร—ไม่ต้องใช้ทริคขั้นสูง ตอนนี้เรามาต่อด้วยการเชื่อมโยงพฤติกรรมด้วย JavaScript
ตอนนี้คุณมีช่องกรอก ปุ่ม และรายการบนหน้า คุณจะทำให้มัน "ทำงาน" เป้าหมายคือ: เมื่อใครสักคนพิมพ์งานแล้วกด Add (หรือกด Enter) รายการใหม่จะปรากฏ
ในไฟล์ JavaScript ของคุณ ให้ดึงองค์ประกอบที่ต้องการ จากนั้นฟังสองเหตุการณ์: การคลิกปุ่มและการกด Enter ในช่องกรอก
const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');
function addTask() {
const text = taskInput.value.trim();
// Block empty tasks (including ones that are just spaces)
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
taskList.appendChild(li);
// Clear the input and put the cursor back
taskInput.value = '';
taskInput.focus();
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
addTask();
}
});
trim() เพื่อตัดช่องว่างส่วนเกินที่ต้น/ท้าย\u003cli\u003e ใหม่, ตั้งข้อความ และเพิ่มเข้าไปในรายการถ้าไม่เกิดอะไรขึ้น ให้ตรวจสอบว่า ID ขององค์ประกอบใน HTML ตรงกับตัวเลือกใน JavaScript หรือไม่ (นี่เป็นข้อผิดพลาดที่ผู้เริ่มต้นเจอบ่อย)
เมื่อคุณเพิ่มงานได้แล้ว มาทำให้มันโต้ตอบได้มากขึ้น: ต้องทำเครื่องหมายงานว่าเสร็จและลบงานได้
แทนที่จะเก็บงานเป็นสตริงล้วน ให้เก็บเป็นออบเจ็กต์ นั่นทำให้แต่ละงานมีตัวตนที่ชัดเจนและมีที่เก็บสถานะ "done":
text: ข้อความของงานdone: true หรือ falseid: ตัวเลขที่ไม่ซ้ำ เพื่อหา/ลบงานที่ถูกต้องตัวอย่างง่ายๆ:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
เวลาที่เราสร้าง DOM ของแต่ละงาน ให้ใส่ checkbox หรือปุ่ม “Done” และปุ่ม “Delete” ด้วย
ตัวอย่างการฟังเหตุการณ์คือ event delegation: ใส่ listener ตัวเดียวที่คอนเทนเนอร์ของรายการ แล้วตรวจสอบว่าคลิกที่อะไร
function toggleDone(id) {
tasks = tasks.map(t => t.id === id ? { ...t, done: !t.done } : t);
renderTasks();
}
function deleteTask(id) {
tasks = tasks.filter(t => t.id !== id);
renderTasks();
}
document.querySelector("#taskList").addEventListener("click", (e) => {
const id = Number(e.target.dataset.id);
if (e.target.matches(".toggle")) toggleDone(id);
if (e.target.matches(".delete")) deleteTask(id);
});
ในฟังก์ชัน renderTasks():
data-id="${task.id}" ให้แต่ละปุ่ม.done)ตอนนี้รายการของคุณมีปัญหาเดิม: รีเฟรชหน้าหรือปิดแท็บแล้วทุกอย่างหาย
เพราะงานถูกเก็บอยู่ในหน่วยความจำ JavaScript เท่านั้น เมื่อหน้าโหลดใหม่ หน่วยความจำถูกรีเซ็ต
localStorage มีอยู่แล้วในเบราว์เซอร์ คิดว่ามันเป็นกล่องที่เก็บข้อความภายใต้ชื่อ ("key") เหมาะสำหรับโปรเจกต์ผู้เริ่มต้นเพราะไม่ต้องมีเซิร์ฟเวอร์หรือระบบบัญชีผู้ใช้
เราจะเก็บรายการงานทั้งหมดเป็นข้อความ JSON แล้วโหลดกลับเมื่อหน้าเปิด
เมื่อใดก็ตามที่คุณเพิ่มงาน ทำเครื่องหมายเสร็จ หรือ ลบ ให้เรียก saveTasks()
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
ที่ใดก็ตามที่แอปอัปเดตอาเรย์ tasks ให้เรียกนี้ทันทีหลังจากนั้น:
saveTasks(tasks);
renderTasks(tasks);
เมื่อหน้าโหลด ให้อ่านค่าที่บันทึกไว้ หากยังไม่มี ให้ใช้รายการว่าง
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
แค่นั้นแหละ: แอปของคุณจะจำงานข้ามการรีเฟรช
เคล็ดลับ: localStorage เก็บได้แค่สตริง ดังนั้น JSON.stringify() จะเปลี่ยนอาเรย์เป็นข้อความ และ JSON.parse() จะเปลี่ยนกลับเป็นอาเรย์เมื่อโหลด
การทดสอบฟังดูน่าเบื่อ แต่เป็นวิธีที่เร็วที่สุดในการเปลี่ยนแอปจาก "ทำงานเฉพาะเครื่องฉัน" ให้เป็น "ใช้งานได้ทุกครั้ง" ทำการตรวจสอบสั้นๆ หลังการเปลี่ยนแปลงแต่ละครั้ง
ทำตามขั้นตอนหลักนี้:
ถ้าขั้นตอนใดล้มเหลว ให้แก้ก่อนจะเพิ่มฟีเจอร์ใหม่ แอปเล็กๆ จะรกเมื่อคุณสะสมปัญหา
กรณีขอบคือข้อมูลที่คุณไม่ได้ออกแบบไว้ แต่ผู้ใช้จริงจะทำ:
การแก้ทั่วไปคือบล็อกงานว่าง:
const text = input.value.trim();
addButton.disabled = text === "";
(คุณสามารถรันคำสั่งนี้ทุกครั้งที่เกิด input event และอีกครั้งก่อนจะเพิ่ม)
เมื่อการคลิกไม่ตอบสนอง มักเป็นสาเหตุเหล่านี้:
id หรือ class ใน HTML ต่างจาก JS)app.js หาไม่พบ)เมื่อบางอย่างดูไม่แน่นอน ให้ log มัน:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
ดู Console ของเบราว์เซอร์หา error (ข้อความสีแดง) เมื่อแก้แล้วให้ลบ log เหล่านั้นออกเพื่อไม่ให้รกโปรเจกต์
แอป to‑do ถึงจะ "เสร็จ" ก็ต่อเมื่อมันสบายสำหรับคนจริงๆ ใช้—บนมือถือ ด้วยคีย์บอร์ด และกับเครื่องมือช่วยอ่านหน้าจอ
บนหน้าจอเล็ก ปุ่มเล็กทำให้หงุดหงิด ให้พื้นที่คลิกพอสมควร:
ถ้าคุณใช้ CSS การเพิ่ม padding, font-size, และ gap มักจะช่วยได้มากที่สุด
เครื่องมืออ่านหน้าจอต้องการชื่อที่ชัดเจนสำหรับคอนโทรล
\u003clabel\u003e จริง (เป็นตัวเลือกที่ดีที่สุด) ถ้าไม่อยากแสดง ให้ซ่อนด้วย CSS ทางสายตาแต่เก็บไว้ใน HTMLaria-label="Delete task" เพื่อให้เครื่องมืออ่านหน้าจอไม่ประกาศเป็น "ปุ่ม" อย่างไม่มีบริบทนี่ช่วยให้คนอื่นเข้าใจว่าควบคุมแต่ละอันทำอะไรโดยไม่ต้องเดา
ให้แน่ใจว่าใช้งานได้โดยไม่ต้องใช้เมาส์:
\u003cform\u003e เพื่อให้ Enter ทำงานโดยธรรมชาติ)ใช้ขนาดตัวอ่านง่าย (16px เป็นจุดเริ่มต้นที่ดี) และความแตกต่างของสีที่ชัดเจน (ข้อความเข้มบนพื้นหลังอ่อน หรือกลับกัน) หลีกเลี่ยงการใช้สีเพียงอย่างเดียวเพื่อบอกว่า "เสร็จ"—เพิ่มสไตล์ชัดเจนอย่างขีดทับด้วย
เมื่อทุกอย่างทำงาน ให้ใช้เวลา 10–15 นาทีจัดระเบียบ นี่จะทำให้การแก้ไขในอนาคตง่ายขึ้นและช่วยให้คุณเข้าใจโปรเจกต์ของตัวเองเมื่อกลับมาดูอีกครั้ง
เก็บให้เล็กและคาดเดาได้:
/index.html — โครงหน้าของหน้า (ช่องกรอก ปุ่ม รายการ)/styles.css — รูปแบบของแอป (ระยะ ขนาดตัวอักษร สไตล์ "เสร็จ")/app.js — พฤติกรรม (เพิ่ม, สลับเสร็จ, ลบ, บันทึก/โหลด)/README.md — โน้ตสั้นๆ สำหรับ "ตัวคุณในอนาคต"ถ้าคุณชอบย่อยโฟลเดอร์ คุณอาจใช้:
/css/styles.css/js/app.jsแค่ให้แน่ใจว่า \u003clink\u003e และ \u003cscript\u003e พาธตรงกัน
เคล็ดลัดไม่กี่ข้อ:
taskInput, taskList, saveTasks()ตัวอย่างที่อ่านง่าย:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)README.md ของคุณอาจสั้น:
index.html ในเบราว์เซอร์)อย่างน้อยบีบอัดโฟลเดอร์เมื่อจบแต่ละขั้นสำคัญ (เช่น “localStorage ทำงานแล้ว”) ถ้าต้องการประวัติการเปลี่ยนแปลง Git ดีมากแต่ไม่จำเป็น สำรองอย่างน้อยหนึ่งชุดก็ช่วยได้
การเผยแพร่หมายถึงนำไฟล์ของแอป (HTML, CSS, JavaScript) ไปไว้สาธารณะบนอินเทอร์เน็ตเพื่อให้คนอื่นเปิดลิงก์แล้วใช้ได้ เพราะแอปนี้เป็น "static site" (รันในเบราว์เซอร์และไม่ต้องการเซิร์ฟเวอร์) จึงโฮสต์ฟรีได้หลายที่
ขั้นตอนคร่าวๆ:
ถ้าแอปใช้ไฟล์แยก ให้ตรวจสอบชื่อไฟล์ตรงกับลิงก์ เช่น styles.css กับ style.css
ถ้าต้องการวิธีที่ง่ายที่สุด “อัปโหลดแล้วใช้งาน”:
localStorage ทำงาน)ถ้ามันผ่าน ให้ส่งลิงก์ให้เพื่อนลอง—คนใหม่มักพบปัญหาได้เร็ว
คุณสร้างแอป to‑do ที่ใช้งานได้แล้ว ถ้าต้องการเรียนต่อโดยไม่โดดไปโปรเจกต์ใหญ่ ฟีเจอร์เหล่านี้เพิ่มคุณค่าและสอนแนวคิดที่เป็นประโยชน์
เพิ่มปุ่ม "Edit" ใกล้กับแต่ละงาน เมื่อคลิก ให้สลับป้ายงานเป็นช่องกรอกขนาดเล็ก (กรอกค่าล่วงหน้า) พร้อมปุ่ม "Save" และ "Cancel"
เคล็ดลับ: เก็บข้อมูลงานเป็นอาเรย์ของออบเจ็กต์ (มี id และ text) การแก้ไขคือ หา id ที่ตรง อัปเดต text แล้ว render ใหม่และบันทึก
เพิ่มสามปุ่มด้านบน: All, Active, Done
เก็บตัวกรองปัจจุบันในตัวแปรเช่น currentFilter = 'all' เมื่อตอน render ให้แสดง:
ทำให้น้ำหนักเบา:
YYYY-MM-DD) และแสดงข้างงานแม้เพิ่มฟิลด์เดียวก็สอนการอัปเดต data model และ UI พร้อมกัน
เมื่อพร้อม แนวคิดใหญ่คือ: แทนที่จะบันทึกใน localStorage ให้ส่งงานไปยัง API (เซิร์ฟเวอร์) ด้วย fetch() เซิร์ฟเวอร์เก็บในฐานข้อมูล ทำให้ซิงก์ข้ามอุปกรณ์ได้
ถ้าคุณอยากลองกระโดดโดยไม่สร้างใหม่ทั้งหมด แพลตฟอร์ม prototype อย่าง Koder.ai สามารถช่วยสร้างเวอร์ชันถัดไปอย่างรวดเร็ว: อธิบายฟีเจอร์ในแชท (endpoints, ตารางฐานข้อมูล, การเปลี่ยน UI), วางแผน แล้ว export the source code เมื่อต้องการเรียนรู้หรือปรับแต่งโปรเจกต์ React/Go/PostgreSQL ที่สร้างขึ้น
ลองสร้าง notes app (มีการค้นหา) หรือ habit tracker (เช็กทุกวัน) พวกนี้ใช้ทักษะเดียวกัน: การเรนเดอร์รายการ แก้ไข บันทึก และออกแบบ UI พื้นฐาน