หากคุณเคยสร้างฟอร์มติดต่อและเห็นหน้าเว็บรีโหลดทั้งหน้าหลังจากที่ผู้ใช้คลิก "ส่ง" คุณคงรู้ว่าประสบการณ์นั้นไม่ดีเท่าไหร่ การใช้เทคนิค HTML form submit fetch javascript ช่วยให้คุณส่งข้อมูลฟอร์มไปยัง backend endpoint ในพื้นหลัง ให้ผู้ใช้อยู่ในหน้าเดิม และแสดงข้อความสำเร็จหรือข้อผิดพลาดแบบเรียบร้อยโดยไม่ต้องรีเฟรชหน้าเว็บ บทช่วยสอนนี้จะพาคุณผ่านทุกขั้นตอน ตั้งแต่การเขียน HTML พื้นฐานไปจนถึงการใช้ fetch() เรียก Sendform endpoint เพื่อให้การส่งข้อมูลไปถึงอีเมลหรือ workflow ที่เชื่อมต่อไว้ของคุณโดยตรง
สิ่งสำคัญที่ต้องจำ:
- Fetch API ช่วยให้คุณส่งข้อมูลฟอร์มได้โดยไม่ต้องรีโหลดหน้า ทำให้ผู้ใช้ได้รับประสบการณ์ที่ราบรื่นขึ้น
- การจับ event
submitและเรียกpreventDefault()คือพื้นฐานของการส่งฟอร์มแบบ ajax - Sendform มี endpoint URL พร้อมใช้ คุณไม่ต้องเขียน backend code เลยเพื่อรับและเก็บข้อมูลที่ส่งมา
- การจัดการ response ที่เหมาะสม (แสดงข้อความสำเร็จหรือข้อผิดพลาด) สำคัญเท่ากับการส่งข้อมูลให้ถูกต้อง
สารบัญ
ทำไมใช้ fetch() แทนการส่งฟอร์มแบบปกติ
พฤติกรรมการส่งฟอร์มแบบปกติของเบราว์เซอร์ทำสิ่งเดียว คือ serialize ข้อมูลในฟิลด์ ส่ง POST (หรือ GET) request ไปยัง URL ใน action แล้วโหลดสิ่งที่ server ส่งกลับมา หมายความว่าผู้ใช้จะเห็นหน้าจอกะพริบสีขาว สูญเสียตำแหน่งการเลื่อนหน้า และต้องรอให้หน้าใหม่โหลดขึ้นมา ในเครือข่ายที่ช้า อาจรู้สึกเหมือนเว็บเสีย
Fetch API แก้ปัญหานี้ด้วยการทำ HTTP requests ผ่าน JavaScript โดยตรง โดยไม่ต้องนำทางออกไปจากหน้า ทำให้เกิดรูปแบบ ส่งฟอร์มโดยไม่รีโหลดหน้า ที่ช่วยให้ผู้ใช้มีส่วนร่วมและคุณสามารถควบคุมประสบการณ์ผู้ใช้ทุกด้าน รวมถึงสถานะการโหลด การแสดงข้อผิดพลาดแบบ inline และแบนเนอร์สำเร็จแบบมีแอนิเมชั่น
เหตุผลเชิงปฏิบัติเพิ่มเติมที่ควรใช้ fetch():
- คุณสามารถแนบ custom headers (เช่น CSRF token หรือ authorization key) ที่ HTML form ธรรมดาไม่สามารถส่งได้
- คุณสามารถ serialize ข้อมูลเป็น JSON,
FormData, หรือ URL-encoded strings ขึ้นอยู่กับสิ่งที่ endpoint คาดหวัง - การจัดการข้อผิดพลาดชัดเจน คุณเป็นคนตัดสินใจว่า "ความล้มเหลว" หมายถึงอะไรและจะสื่อสารอย่างไร
- ใช้งานได้กับเว็บไซต์แบบ static ทุกประเภท รวมถึงที่โฮสต์บน GitHub Pages, Netlify หรือ CDN เพราะตรรกะทั้งหมดอยู่ในเบราว์เซอร์
หมายเหตุ: หากคุณใช้งานเว็บไซต์บิลเดอร์อย่าง Webflow, WordPress หรือ Hugo วิธี fetch() เดียวกันนี้ก็ใช้ได้ ดูคู่มือของเราเกี่ยวกับ วิธีผสาน Sendform กับเว็บไซต์บิลเดอร์ของคุณ สำหรับเทคนิคเฉพาะแพลตฟอร์ม
การตั้งค่า HTML Form พื้นฐาน
ก่อนที่จะเขียน JavaScript สักบรรทัด คุณต้องมี HTML form ที่มีโครงสร้างที่ดี รายละเอียดสำคัญคือคุณไม่จำเป็นต้องมี attribute action ชี้ไปที่ไหน เพราะ JavaScript จะจัดการการส่งข้อมูล แต่คุณต้องการ attribute name ที่มีความหมายในทุก input เพราะสิ่งเหล่านี้จะกลายเป็น field keys ในข้อมูลที่ส่ง
<form id="contact-form" novalidate>
<div>
<label for="name">ชื่อของคุณ</label>
<input type="text" id="name" name="name" required placeholder="สมชาย ใจดี">
</div>
<div>
<label for="email">ที่อยู่อีเมล</label>
<input type="email" id="email" name="email" required placeholder="[email protected]">
</div>
<div>
<label for="message">ข้อความ</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">ส่งข้อความ</button>
<!-- พื้นที่แสดงข้อมูลป้อนกลับ -->
<div id="form-feedback" aria-live="polite"></div>
</form>สิ่งที่น่าสังเกตในมาร์กอัปนี้:
novalidateใน form element จะปิดการแสดงข้อความตรวจสอบของเบราว์เซอร์ ให้คุณควบคุมการแสดงข้อผิดพลาดใน JavaScript ได้อย่างเต็มที่- div
id="form-feedback"พร้อมaria-live="polite"คือที่ที่ข้อความสำเร็จและข้อผิดพลาดจะปรากฏ ARIA attribute ช่วยให้ screen reader ประกาศข้อมูลป้อนกลับโดยอัตโนมัติ - input ทุกตัวมีทั้ง
id(สำหรับการเชื่อมโยง label) และname(สำหรับข้อมูลฟอร์ม)
การจับ Submit Event
ขั้นตอนแรกของ การส่งฟอร์มด้วย javascript คือการสกัดพฤติกรรมเริ่มต้นของเบราว์เซอร์ คุณทำสิ่งนี้โดยฟัง event submit ใน form element และเรียก event.preventDefault() ทันที
const form = document.getElementById('contact-form');
form.addEventListener('submit', async function (event) {
event.preventDefault(); // หยุดการนำทางหน้าเว็บแบบปกติ
// การตรวจสอบพื้นฐานฝั่งไคลเอนต์
const name = form.elements['name'].value.trim();
const email = form.elements['email'].value.trim();
const message = form.elements['message'].value.trim();
if (!name || !email || !message) {
showFeedback('กรุณากรอกข้อมูลให้ครบทุกช่อง', 'error');
return;
}
// ดำเนินการส่งข้อมูล (หัวข้อถัดไป)
await submitForm({ name, email, message });
});การแยกตรรกะการตรวจสอบออกจากการเรียกเครือข่ายช่วยให้โค้ดอ่านง่ายและขยายได้ คำสำคัญ async ใน event handler ให้คุณใช้ await ข้างในได้ ทำให้การเรียก Fetch API ดูเหมือนแบบ synchronous และหลีกเลี่ยง promise chains ที่ซ้อนกันลึก
การใช้ fetch() กับ Sendform Endpoint
นี่คือจุดที่งานจริงเกิดขึ้น แทนที่จะสร้าง server เองเพื่อรับ เก็บ และส่งต่อการส่งฟอร์ม คุณสามารถใช้ Sendform เป็น backend ของคุณ หลังจากสร้างฟอร์มใน Sendform dashboard คุณจะได้ endpoint URL ที่ไม่ซ้ำกัน URL นั้นคือทั้งหมดที่คุณต้องการ
ฟังก์ชันการส่งข้อมูลด้านล่างใช้ FormData API เพื่อสร้าง payload ที่ Sendform รับได้โดยตรง:
async function submitForm(data) {
// แทนที่ URL นี้ด้วย Sendform endpoint จริงของคุณ
const SENDFORM_ENDPOINT = 'https://sendform.net/th/YOUR_FORM_ID';
const formData = new FormData();
formData.append('name', data.name);
formData.append('email', data.email);
formData.append('message', data.message);
try {
const response = await fetch(SENDFORM_ENDPOINT, {
method: 'POST',
body: formData,
});
if (response.ok) {
showFeedback('ขอบคุณครับ! ข้อความของคุณถูกส่งแล้ว', 'success');
form.reset();
} else {
const errorData = await response.json().catch(() => ({}));
const errorMsg = errorData.message || 'เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้ง';
showFeedback(errorMsg, 'error');
}
} catch (networkError) {
showFeedback('เกิดข้อผิดพลาดเครือข่าย ตรวจสอบการเชื่อมต่อและลองใหม่', 'error');
}
}การตัดสินใจสำคัญในโค้ดนี้:
- ไม่ได้ตั้งค่า header
Content-Typeด้วยตนเอง เมื่อคุณส่งFormDataobject เป็น body เบราว์เซอร์จะตั้งค่าmultipart/form-databoundary ที่ถูกต้องโดยอัตโนมัติ - การตรวจสอบ
response.okครอบคลุม HTTP status codes 2xx ทั้งหมด ไม่ใช่แค่ 200 นี่แข็งแกร่งกว่าการเปรียบเทียบresponse.status === 200 try/catchภายนอกจับความล้มเหลวระดับเครือข่าย (DNS errors, สถานะออฟไลน์) ที่response.okไม่เคยเห็น
การจัดการ Response - ข้อความสำเร็จและข้อผิดพลาด
การส่งข้อมูลเป็นแค่ครึ่งหนึ่งของงาน ผู้ใช้ต้องการข้อมูลป้อนกลับที่ชัดเจนและทันที ฟังก์ชันช่วย showFeedback() ที่อ้างถึงข้างต้นเขียนข้อความลงใน feedback div ที่คุณเพิ่มเข้าไปใน HTML:
function showFeedback(message, type) {
const feedbackEl = document.getElementById('form-feedback');
feedbackEl.textContent = message;
feedbackEl.className = type === 'success' ? 'feedback-success' : 'feedback-error';
}นี่เป็นแบบมินิมอลโดยเจตนา ในโปรเจกต์จริง คุณอาจเปลี่ยน textContent เป็นคอมโพเนนต์แอนิเมชั่นหรือไลบรารี toast notification แต่รูปแบบยังคงเดิม: อัปเดต DOM ตามผลลัพธ์ของการเรียก fetch()
สำหรับสถานการณ์ขั้นสูง เช่น การเปลี่ยนเส้นทางไปยังหน้าขอบคุณแบบกำหนดเองหรือการทริกเกอร์ automation ตามลำดับหลังการส่งข้อมูล ดูบทความของเราเกี่ยวกับ วิธีทำ automate form workflows ด้วย webhooks, Zapier และ APIs
Best Practices และเทคนิค
การทำให้โค้ดทำงานได้เป็นสิ่งหนึ่ง การส่งมันในรูปแบบที่ยืนหยัดได้ใน production เป็นอีกสิ่งหนึ่ง นี่คือเทคนิคสำคัญที่ต้องจำไว้:
- ปิดใช้งานปุ่มส่งระหว่างการร้องขอ ตั้ง
button.disabled = trueก่อนเรียกfetch()และเปิดใช้งานอีกครั้งในfinallyblock นี่ป้องกันการส่งซ้ำหากผู้ใช้คลิกหลายครั้ง - แสดงสถานะการโหลด เปลี่ยนข้อความปุ่มเป็น "กำลังส่ง..." หรือเพิ่ม spinner class ขณะที่ request กำลังดำเนินการ ผู้ใช้ที่ไม่เห็นข้อมูลป้อนกลับมักคิดว่าไม่มีอะไรเกิดขึ้นและคลิกอีกครั้ง
- ตรวจสอบฝั่ง server ด้วย การตรวจสอบฝั่งไคลเอนต์เป็นเรื่องของประสบการณ์ผู้ใช้ Sendform และบริการ backend ใดๆ ควรถือว่าข้อมูลที่เข้ามาทั้งหมดไม่น่าเชื่อถือ
- ใช้ HTTPS ทุกที่ การส่งข้อมูลฟอร์มผ่าน HTTP ธรรมดาเปิดเผยข้อมูลผู้ใช้ระหว่างการส่ง Sendform endpoints เป็น HTTPS โดยค่าเริ่มต้น แต่ให้แน่ใจว่าหน้าเว็บของคุณเองก็ใช้ HTTPS เช่นกัน
- เพิ่มการป้องกันสแปม honeypot field หรือการผสาน CAPTCHA ช่วยลดการส่งข้อมูลขยะได้อย่างมาก สำหรับการดูลึกในหัวข้อนี้ ดูคู่มือของเราเกี่ยวกับ best practices การป้องกันสแปมสำหรับฟอร์ม
- ทดสอบเส้นทางข้อผิดพลาดโดยเจตนา เปลี่ยน endpoint URL ชั่วคราวเป็นสิ่งที่ไม่ถูกต้องและยืนยันว่าข้อความข้อผิดพลาดของคุณปรากฏขึ้น นักพัฒนาส่วนใหญ่ทดสอบแค่เส้นทางที่สำเร็จ
- เก็บ endpoint URL ออกจาก version control หากโปรเจกต์ของคุณเป็น open source ให้เก็บ Sendform endpoint ใน environment variable หรือไฟล์ config ที่อยู่ใน
.gitignore
สำหรับผู้ใช้ static site: หากโปรเจกต์ของคุณเป็น Hugo, Eleventy หรือ HTML site ธรรมดาที่ไม่มี server วิธี fetch() ที่อธิบายไว้ที่นี่เป็นวิธีที่แนะนำ อ่านเพิ่มเติมในคู่มือของเราเกี่ยวกับ serverless form handling สำหรับ static sites
สรุป
การแทนที่การส่งฟอร์มแบบปกติด้วยการเรียก fetch() เป็นหนึ่งในการปรับปรุงที่ส่งผลกระทบสูงที่สุดที่คุณสามารถทำได้กับฟอร์มติดต่อหรือจับลีดใดๆ ผลลัพธ์คือประสบการณ์ที่เร็วและเป็นมืออาชีพมากขึ้น ที่ช่วยให้ผู้ใช้อยู่ในหน้าของคุณและให้คุณควบคุมการแสดงข้อมูลป้อนกลับได้อย่างเต็มที่ จับคู่กับ Sendform endpoint และคุณจะไม่ต้องใช้ server-side code เลย ฟอร์มของคุณพร้อมใช้งาน การส่งข้อมูลไปถึงกล่องจดหมายของคุณ และผู้ใช้ไม่เคยเห็นการรีโหลดหน้าที่น่ารำคาญ สร้าง Sendform endpoint ฟรีของคุณวันนี้ และรับการส่งข้อมูลครั้งแรกในไม่กี่นาที
คำถามที่พบบ่อย
ได้ครับ เนื่องจาก fetch() ทำงานทั้งหมดในเบราว์เซอร์ จึงใช้งานได้กับ static HTML sites, JAMstack projects และแพลตฟอร์มใดที่ให้บริการ HTML คุณไม่ต้องมี server ของตัวเอง ข้อกำหนดเพียงอย่างเดียวคือ endpoint (เช่น Sendform URL) ที่สามารถรับ POST request ได้
fetch() เป็นตัวแทนสมัยใหม่ของ XMLHttpRequest ใช้ Promises รองรับ async/await และมี API ที่สะอาดกว่า สำหรับโปรเจกต์ใหม่ ควรใช้ fetch() เสมอ ทั้งคู่ให้ผลลัพธ์การส่งฟอร์มแบบ ajax เดียวกัน แต่ fetch() ต้องการ boilerplate code น้อยกว่ามาก
ไม่ครับ เมื่อคุณส่ง FormData object เป็น body เบราว์เซอร์จะตั้งค่า Content-Type เป็น multipart/form-data โดยอัตโนมัติและรวม boundary string ที่ถูกต้อง การตั้งค่าด้วยตนเองจะทำให้ request เสียโดยการไม่ใส่ค่า boundary นั้น
สมัครสมาชิกที่ Sendform สร้างฟอร์มใหม่ใน dashboard และคัดลอก endpoint URL ที่สร้างขึ้น วาง URL นั้นเป็นเป้าหมายในการเรียก fetch() ของคุณ การส่งข้อมูลจะถูกส่งต่อไปยังที่อยู่อีเมลที่กำหนดค่าไว้ทันที
ความล้มเหลวของเครือข่ายทำให้ fetch() Promise ถูก reject ซึ่งถูกจับโดย try/catch block ภายนอกในโค้ดตัวอย่าง ผู้ใช้จะเห็นข้อความ "เกิดข้อผิดพลาดเครือข่าย" ที่คุณกำหนดไว้ การส่งข้อมูลจะไม่ถูกจัดคิวโดยอัตโนมัติ ผู้ใช้ต้องลองใหม่เมื่อการเชื่อมต่อกลับมา