Now.js Framework Documentation
FormError
FormError
ภาพรวม
FormError เป็นระบบจัดการข้อผิดพลาดที่ครอบคลุมสำหรับฟอร์มใน Now.js Framework มันให้ชุดเครื่องมือที่สมบูรณ์สำหรับการแสดง จัดการ และล้างข้อผิดพลาดการตรวจสอบและข้อความสำเร็จในฟอร์ม ระบบรองรับการจัดการข้อผิดพลาดทั้งระดับฟิลด์และระดับฟอร์มพร้อมการจัดการโฟกัสอัตโนมัติ การเลื่อน คุณสมบัติการเข้าถึง และฟังก์ชันล้างอัตโนมัติที่ปรับแต่งได้
คุณสมบัติหลัก
- การแสดงข้อผิดพลาดระดับฟิลด์ - แสดงข้อผิดพลาดการตรวจสอบข้างๆ ฟิลด์ของฟอร์ม
- การแสดงข้อความทั่วไป - แสดงข้อผิดพลาดหรือข้อความสำเร็จระดับฟอร์ม
- การจัดการโฟกัสอัตโนมัติ - โฟกัสไปยังฟิลด์ที่มีข้อผิดพลาดแรกโดยอัตโนมัติ
- การเลื่อนอัจฉริยะ - เลื่อนไปยังฟิลด์ที่มีข้อผิดพลาดพร้อม offset ที่ปรับได้
- การรองรับการเข้าถึง - แอตทริบิวต์ ARIA สำหรับ screen reader (aria-invalid, aria-errormessage)
- ล้างข้อความอัตโนมัติ - ล้างข้อความอัตโนมัติหลังจากหน่วงเวลาที่กำหนด
- การคืนค่าเนื้อหาเดิม - เก็บรักษาและคืนค่าเนื้อหาเดิมของ container
- ระดับการตั้งค่าหลายแบบ - การตั้งค่าแบบ global และเฉพาะฟอร์ม
- การผสานรวมระบบเหตุการณ์ - ปล่อยเหตุการณ์สำหรับการดำเนินการข้อผิดพลาดทั้งหมด
- การตรวจหา Container แบบยืดหยุ่น - ตรวจหาหรือระบุ container ข้อผิดพลาด/สำเร็จอัตโนมัติ
กรณีการใช้งาน
- การตรวจสอบฟอร์ม - แสดงข้อผิดพลาดการตรวจสอบแบบ inline สำหรับฟิลด์ฟอร์ม
- ข้อผิดพลาดจาก Response เซิร์ฟเวอร์ - แสดงข้อความข้อผิดพลาดจาก response ของ API
- การแจ้งเตือนความสำเร็จ - แสดงข้อความสำเร็จหลังจากส่งฟอร์ม
- ฟอร์มหลายขั้นตอน - จัดการข้อผิดพลาดในฟอร์มหลายขั้นตอน
- การตรวจสอบแบบเรียลไทม์ - แสดง/ล้างข้อผิดพลาดเมื่อผู้ใช้พิมพ์หรือออกจากฟิลด์
- การปฏิบัติตามมาตรฐานการเข้าถึง - ทำให้ฟอร์มเข้าถึงได้ด้วยแอตทริบิวต์ ARIA ที่เหมาะสม
การติดตั้งและนำเข้า
FormError โหลดมาพร้อมกับ Now.js Framework และพร้อมใช้งานทันทีผ่าน window object:
// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.FormError); // อ็อบเจ็กต์ FormError ที่ผูกไว้กับ windowเริ่มต้นอย่างรวดเร็ว
<!-- ฟอร์มพร้อม container ข้อผิดพลาด -->
<form id="loginForm">
<div class="form-control">
<label for="email">อีเมล</label>
<input type="email" id="email" name="email">
<div id="result_email" class="comment"></div>
</div>
<div class="form-control">
<label for="password">รหัสผ่าน</label>
<input type="password" id="password" name="password">
<div id="result_password" class="comment"></div>
</div>
<div id="form-message" class="form-message"></div>
</form>
<script>
// แสดงข้อผิดพลาดฟิลด์
FormError.showFieldError('email', 'กรุณากรอกอีเมลที่ถูกต้อง');
// แสดงข้อผิดพลาดทั่วไป
FormError.showGeneralError('ข้อมูลเข้าสู่ระบบไม่ถูกต้อง', document.getElementById('loginForm'));
// ล้างข้อผิดพลาดฟิลด์เฉพาะ
FormError.clearFieldError('email');
// ล้างข้อผิดพลาดทั้งหมด
FormError.clearAll();
</script>การแสดงข้อความสำเร็จ
<script>
// แสดงข้อความสำเร็จ
FormError.showSuccess('เข้าสู่ระบบสำเร็จ! กำลังเปลี่ยนเส้นทาง...', document.getElementById('loginForm'));
// ล้างอัตโนมัติหลัง 3 วินาที
setTimeout(() => {
FormError.clearSuccess(document.getElementById('loginForm'));
}, 3000);
</script>การตั้งค่า
การตั้งค่าแบบ Global
ตั้งค่าพฤติกรรมเริ่มต้นสำหรับฟอร์มทั้งหมด:
FormError.configure({
// คลาส CSS สำหรับฟิลด์ที่ไม่ผ่านการตรวจสอบ (ค่าเริ่มต้น: 'invalid')
errorClass: 'has-error',
// คลาส CSS สำหรับข้อความข้อผิดพลาด (ค่าเริ่มต้น: 'error')
errorMessageClass: 'error-text',
// โฟกัสฟิลด์ข้อผิดพลาดตัวแรกโดยอัตโนมัติ (ค่าเริ่มต้น: true)
autoFocus: true,
// เลื่อนไปยังข้อผิดพลาดตัวแรกโดยอัตโนมัติ (ค่าเริ่มต้น: true)
autoScroll: true,
// ระยะเยื้องในการเลื่อน (พิกเซล) (ค่าเริ่มต้น: 100)
scrollOffset: 120,
// เปิดโหมดดีบัก (ค่าเริ่มต้น: false)
debug: true,
// แสดงข้อผิดพลาดถัดจากฟิลด์โดยตรง (ค่าเริ่มต้น: true)
showErrorsInline: true,
// แสดงข้อผิดพลาดผ่านระบบแจ้งเตือน (ค่าเริ่มต้น: false)
showErrorsInNotification: false,
// ล้างข้อผิดพลาดอัตโนมัติหลังหน่วงเวลา (ค่าเริ่มต้น: true)
autoClearErrors: true,
// ระยะเวลาล้างอัตโนมัติ (มิลลิวินาที) (ค่าเริ่มต้น: 5000)
autoClearErrorsDelay: 3000,
// ID ของ container ข้อผิดพลาดเริ่มต้น (ค่าเริ่มต้น: 'form-message')
defaultErrorContainer: 'global-error',
// ID ของ container ข้อความสำเร็จเริ่มต้น (ค่าเริ่มต้น: 'form-message')
defaultSuccessContainer: 'global-success'
});การตั้งค่าเฉพาะฟอร์ม
FormError จะรับค่าการตั้งค่าจากอินสแตนซ์ของ FormManager โดยอัตโนมัติ:
<form
data-form="registrationForm"
data-error-container="registration-errors"
data-success-container="registration-success"
data-auto-focus="true"
data-auto-scroll="false">
<!-- ฟิลด์ของฟอร์ม -->
</form>
<script>
// FormError จะใช้การตั้งค่าเฉพาะฟอร์มเมื่อแสดงข้อผิดพลาด
const form = document.querySelector('[data-form="registrationForm"]');
// ใช้ค่า container สำหรับข้อผิดพลาดที่กำหนดไว้ในฟอร์ม
FormError.showGeneralError('การลงทะเบียนล้มเหลว', form);
</script>การตั้งค่า Priority
- การตั้งค่าเฉพาะฟอร์ม (จากอินสแตนซ์ FormManager)
- การตั้งค่าแบบ global (จาก
FormError.config) - การตั้งค่าเริ่มต้น (ค่าที่ฝังอยู่ในโค้ด)
การจัดการข้อผิดพลาดของฟิลด์
แสดงข้อผิดพลาดของฟิลด์
แสดงข้อผิดพลาดการตรวจสอบสำหรับฟิลด์เฉพาะ:
// ตาม ID ของฟิลด์
FormError.showFieldError('email', 'ต้องระบุอีเมล');
// ตามชื่อฟิลด์
FormError.showFieldError('username', 'ชื่อผู้ใช้ต้องมีอย่างน้อย 6 ตัวอักษร');
// มีข้อความผิดพลาดหลายรายการ (แสดงรายการแรก)
FormError.showFieldError('password', [
'ต้องระบุรหัสผ่าน',
'รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร',
'รหัสผ่านต้องมีตัวเลขอย่างน้อย 1 ตัว'
]);
// ด้วยบริบทฟอร์ม
const form = document.getElementById('myForm');
FormError.showFieldError('email', 'อีเมลไม่ถูกต้อง', form);
// พร้อมตัวเลือกเพิ่มเติม
FormError.showFieldError('email', 'อีเมลไม่ถูกต้อง', form, {
focus: false, // ไม่ต้องโฟกัสอัตโนมัติ
scroll: false // ไม่ต้องเลื่อนอัตโนมัติ
});โครงสร้าง Container ข้อผิดพลาด
FormError คาดหวังโครงสร้าง HTML นี้สำหรับข้อผิดพลาดของฟิลด์:
<div class="form-control">
<label for="fieldName">ป้ายกำกับฟิลด์</label>
<input type="text" id="fieldName" name="fieldName">
<!-- container ข้อความข้อผิดพลาด (ต้องมี id="result_{fieldName}") -->
<div id="result_fieldName" class="comment"></div>
</div>สิ่งที่เกิดขึ้นเมื่อแสดงข้อผิดพลาดฟิลด์
- ค้นหาฟิลด์ - ค้นหาองค์ประกอบโดย ID หรือแอตทริบิวต์ name
- เก็บสถานะเดิม - บันทึกเนื้อหาข้อความเดิมหากมีอยู่
- เพิ่มคลาสข้อผิดพลาด - เพิ่ม
errorClassให้กับฟิลด์ พาเรนต์.form-controlและ container ฟิลด์ - แสดงข้อความ - แสดงข้อความข้อผิดพลาดใน container
result_{fieldName} - ตั้งค่าแอตทริบิวต์ ARIA - เพิ่ม
aria-invalid="true"และaria-errormessage - การจัดการโฟกัส - โฟกัสฟิลด์ข้อผิดพลาดแรก (ถ้า
autoFocusเปิดใช้งาน) - เลื่อนไปที่ฟิลด์ - เลื่อนไปที่ฟิลด์ข้อผิดพลาด (ถ้า
autoScrollเปิดใช้งาน) - ปล่อยเหตุการณ์ - ยิงเหตุการณ์
form:errorพร้อมรายละเอียดข้อผิดพลาด
ล้างข้อผิดพลาดของฟิลด์
ลบข้อผิดพลาดการตรวจสอบออกจากฟิลด์เฉพาะ:
// โดย ID หรือชื่อฟิลด์
FormError.clearFieldError('email');
// โดยองค์ประกอบฟิลด์
const emailField = document.getElementById('email');
FormError.clearFieldError(emailField);สิ่งที่เกิดขึ้นเมื่อล้างข้อผิดพลาดฟิลด์
- ลบคลาสข้อผิดพลาด - ลบ
errorClassออกจากฟิลด์ พาเรนต์ และ container - คืนค่าข้อความเดิม - คืนค่าเนื้อหาเดิมของ container
result_{fieldName} - ลบแอตทริบิวต์ ARIA - ลบ
aria-invalidและaria-errormessage - ล้างสถานะ - ลบออกจากแผนที่ข้อผิดพลาดภายใน
- ปล่อยเหตุการณ์ - ยิงเหตุการณ์
form:clearError
ข้อความข้อผิดพลาดทั่วไป
แสดงข้อผิดพลาดทั่วไป
แสดงข้อความข้อผิดพลาดระดับฟอร์ม:
// ใช้กับองค์ประกอบฟอร์ม
const form = document.getElementById('loginForm');
FormError.showGeneralError('ข้อมูลเข้าสู่ระบบไม่ถูกต้อง', form);
// ใช้กับ ID ของ container
FormError.showGeneralError('เกิดข้อผิดพลาดของเซิร์ฟเวอร์', 'error-container');
// ไม่ระบุฟอร์ม (ใช้ container ค่าเริ่มต้น)
FormError.showGeneralError('เกิดข้อผิดพลาดที่ไม่คาดคิด');
// เปิดใช้งานการล้างอัตโนมัติ
FormError.showGeneralError('กรุณาแก้ไขข้อผิดพลาดด้านล่าง', form);
// จะล้างอัตโนมัติหลัง 5 วินาที (ถ้าเปิด autoClearErrors)ลำดับความสำคัญการตรวจหา Container
FormError จะค้นหา container สำหรับข้อผิดพลาดตามลำดับต่อไปนี้:
-
แอตทริบิวต์
data-error-containerของฟอร์ม<form data-error-container="my-errors"> -
องค์ประกอบที่มี
[data-error-container]อยู่ภายในฟอร์ม<div data-error-container class="error-box"></div> -
องค์ประกอบที่มีคลาส
.form-message<div class="form-message"></div> -
องค์ประกอบที่มีคลาส
.error-message<div class="error-message"></div> -
องค์ประกอบที่มีคลาส
.login-message<div class="login-message"></div> -
container ข้อผิดพลาดเริ่มต้น (ตามการตั้งค่า)
FormError.config.defaultErrorContainer // 'form-message'
การเก็บรักษาเนื้อหาเดิม
FormError จะเก็บรักษาเนื้อหาเดิมของ container ไว้:
<div id="form-message" class="form-message">
<p>ยินดีต้อนรับ! กรุณาเข้าสู่ระบบเพื่อดำเนินการต่อ</p>
</div>
<script>
// แสดงข้อผิดพลาด - บันทึกเนื้อหาเดิมไว้ก่อน
FormError.showGeneralError('เข้าสู่ระบบล้มเหลว', document.getElementById('loginForm'));
// ล้างข้อผิดพลาด - คืนค่าเนื้อหาเดิมกลับมา
FormError.clearGeneralError('form-message');
// จะกลับมาแสดงข้อความ "ยินดีต้อนรับ! กรุณาเข้าสู่ระบบเพื่อดำเนินการต่อ" อีกครั้ง
</script>ล้างข้อผิดพลาดทั่วไป
Remove form-level error message:
// ระบุด้วย ID ของ container
FormError.clearGeneralError('form-message');
// ระบุด้วยองค์ประกอบฟอร์ม
const form = document.getElementById('loginForm');
FormError.clearGeneralError(form);
// ระบุด้วยองค์ประกอบ container โดยตรง
const container = document.getElementById('error-container');
FormError.clearGeneralError(container);
// ล้าง container ค่าเริ่มต้น
FormError.clearGeneralError();ข้อความสำเร็จ
แสดงข้อความสำเร็จ
แสดงข้อความสำเร็จระดับฟอร์ม:
// ใช้กับองค์ประกอบฟอร์ม
const form = document.getElementById('loginForm');
FormError.showSuccess('เข้าสู่ระบบสำเร็จ!', form);
// ใช้กับ ID ของ container
FormError.showSuccess('อัปเดตโปรไฟล์เรียบร้อยแล้ว', 'success-container');
// ไม่ระบุฟอร์ม (ใช้ container ค่าเริ่มต้น)
FormError.showSuccess('บันทึกการเปลี่ยนแปลงแล้ว');
// เปิดใช้งานการล้างอัตโนมัติ
FormError.showSuccess('ลงทะเบียนเสร็จสมบูรณ์', form);
// จะล้างอัตโนมัติหลัง 5 วินาที (ถ้าเปิด autoClearErrors)การตรวจหา Container สำเร็จ
มีหลักการเดียวกับ container ข้อผิดพลาด แต่จะค้นหาตามลำดับดังนี้:
- แอตทริบิวต์
data-success-containerของฟอร์ม - องค์ประกอบที่มี
[data-success-container]อยู่ภายในฟอร์ม - องค์ประกอบที่มีคลาส
.form-message - องค์ประกอบที่มีคลาส
.success-message - องค์ประกอบที่มีคลาส
.login-message - container สำเร็จเริ่มต้น (อ้างอิงจากการตั้งค่า)
ล้างข้อความสำเร็จ
Remove success notification:
// ระบุด้วย ID ของ container
FormError.clearSuccess('success-container');
// ระบุด้วยองค์ประกอบฟอร์ม
const form = document.getElementById('loginForm');
FormError.clearSuccess(form);
// ระบุด้วยองค์ประกอบ container โดยตรง
const container = document.getElementById('success-container');
FormError.clearSuccess(container);
// ล้าง container ค่าเริ่มต้น
FormError.clearSuccess();การดำเนินการเป็นกลุ่ม
แสดงข้อผิดพลาดหลายรายการ
แสดงข้อผิดพลาดหลายฟิลด์พร้อมกัน:
const errors = {
email: 'ต้องระบุอีเมล',
password: 'รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร',
username: 'ชื่อผู้ใช้นี้ถูกใช้แล้ว'
};
// แสดงข้อผิดพลาดทั้งหมด (ล้างข้อผิดพลาดเดิมก่อน)
FormError.showErrors(errors);
// ฟิลด์ข้อผิดพลาดอันแรกจะถูกโฟกัสและเลื่อนไปหา
// ข้อผิดพลาดอื่นจะแสดงแบบอินไลน์
// พร้อมตัวเลือกเพิ่มเติม
FormError.showErrors(errors, {
focus: false, // ไม่ต้องโฟกัสฟิลด์ใดเลย
scroll: false // ไม่ต้องเลื่อน
});ล้างข้อความทั้งหมด
Remove all errors and success messages:
// ล้างข้อความข้อผิดพลาดและข้อความสำเร็จทั้งหมดในทั้งเอกสาร
FormError.clearAll();
// ล้างข้อความทั้งหมดเฉพาะในฟอร์มที่ระบุ
const form = document.getElementById('loginForm');
FormError.clearAll(form);ล้างข้อความเฉพาะฟอร์ม
ล้างข้อความทั้งหมดของฟอร์มที่ระบุ:
// ระบุด้วยองค์ประกอบฟอร์ม
const form = document.getElementById('registrationForm');
FormError.clearFormMessages(form);
// ระบุด้วย ID ของฟอร์ม
FormError.clearFormMessages('registrationForm');
// ระบุด้วยค่า data-form ของฟอร์ม
FormError.clearFormMessages('myForm');การจัดการโฟกัสและการเลื่อน
การโฟกัสอัตโนมัติ
ฟิลด์ที่พบข้อผิดพลาดรายการแรกจะถูกโฟกัสให้อัตโนมัติ:
FormError.showFieldError('email', 'ต้องระบุอีเมล');
FormError.showFieldError('password', 'ต้องระบุรหัสผ่าน');
// ฟิลด์อีเมลจะถูกโฟกัสก่อน (เป็นข้อผิดพลาดตัวแรก)ปิดการโฟกัสอัตโนมัติ
// ปรับทั่วทั้งระบบ
FormError.configure({ autoFocus: false });
// ปรับเฉพาะครั้งที่เรียกใช้งาน
FormError.showFieldError('email', 'ข้อผิดพลาด', null, { focus: false });การเลื่อนอัตโนมัติ
พฤติกรรมการเลื่อนแบบอัจฉริยะ:
// จะเลื่อนไปยังองค์ประกอบต่อเมื่อมองไม่เห็นในหน้าจอ
FormError.scrollToElement(element);
// องค์ประกอบจะถือว่าอยู่ในมุมมองหาก:
// - เห็นความสูงอย่างน้อย 30% ใน viewport
// - ส่วนบนหรือส่วนล่างอยู่ใน viewportการตั้งค่าการเลื่อน
FormError.configure({
autoScroll: true, // เปิดการเลื่อนอัตโนมัติ
scrollOffset: 100 // ระยะเยื้องจากด้านบนเป็นพิกเซล
});การเลื่อนด้วยตนเอง
const element = document.getElementById('email');
FormError.scrollToElement(element);คุณสมบัติด้านการเข้าถึง
แอตทริบิวต์ ARIA
FormError จะจัดการแอตทริบิวต์ ARIA ให้อัตโนมัติ:
<!-- Before error -->
<input type="email" id="email" name="email">
<!-- After showing error -->
<input
type="email"
id="email"
name="email"
aria-invalid="true"
aria-errormessage="result_email">
<div id="result_email" role="alert">ต้องระบุอีเมล</div>การรองรับ Screen Reader
// ข้อความข้อผิดพลาดจะถูกอ่านออกเสียงโดยโปรแกรมอ่านหน้าจอ
FormError.showFieldError('email', 'ต้องระบุอีเมล');
// ใช้ aria-live เพื่อติดตามข้อความแบบไดนามิก
// container ข้อผิดพลาดควรกำหนด role="alert" เพื่อประกาศทันทีการนำทางด้วยแป้นพิมพ์
// ฟิลด์ข้อผิดพลาดตัวแรกจะถูกโฟกัสสำหรับผู้ใช้คีย์บอร์ด
FormError.showErrors({
email: 'ต้องระบุอีเมล',
password: 'ต้องระบุรหัสผ่าน'
});
// ฟิลด์อีเมลจะถูกโฟกัส ผู้ใช้สามารถกด Tab ไปยังฟิลด์รหัสผ่านต่อได้การจัดการสถานะ
ตรวจสอบข้อผิดพลาด
// ตรวจสอบว่ามีข้อผิดพลาดหรือไม่
if (FormError.hasErrors()) {
console.log('ฟอร์มมีข้อผิดพลาด');
}
// ดูจำนวนข้อผิดพลาดทั้งหมด
const count = FormError.getErrorsCount();
console.log(`พบข้อผิดพลาดจำนวน ${count} รายการ`);รับรายละเอียดข้อผิดพลาด
// รับข้อมูลข้อผิดพลาดทั้งหมดในปัจจุบัน
const errors = FormError.getErrors();
// ค่าที่ส่งกลับ: [
// { field: 'email', element: <input>, messages: ['ต้องระบุอีเมล'] },
// { field: 'password', element: <input>, messages: ['รหัสผ่านสั้นเกินไป'] }
// ]
// ประมวลผลรายการข้อผิดพลาด
errors.forEach(error => {
console.log(`ฟิลด์: ${error.field}`);
console.log(`ข้อความ: ${error.messages.join(', ')}`);
console.log(`องค์ประกอบ:`, error.element);
});รีเซ็ตสถานะ
ล้างข้อผิดพลาดทั้งหมด and reset internal state:
FormError.reset();
// จะล้างข้อมูลดังนี้:
// - ข้อผิดพลาดของฟิลด์ทั้งหมด
// - ข้อความระดับฟอร์มทั้งหมด
// - แคชข้อความเดิม
// - องค์ประกอบที่ถูกโฟกัสล่าสุด
// - แผนที่สถานะภายในทุกชุดระบบเหตุการณ์
FormError ปล่อยเหตุการณ์สำหรับการดำเนินการทั้งหมด:
เหตุการณ์ที่ใช้ได้
// เมื่อมีการแสดงข้อผิดพลาดของฟิลด์
Now.on('form:error', (data) => {
console.log('ข้อผิดพลาดของฟิลด์:', data.field, data.messages);
console.log('องค์ประกอบ:', data.element);
console.log('ค่าการตั้งค่า:', data.config);
});
// เมื่อเคลียร์ข้อผิดพลาดของฟิลด์
Now.on('form:clearError', (data) => {
console.log('ล้างข้อผิดพลาดแล้ว:', data.field);
console.log('องค์ประกอบ:', data.element);
});
// เมื่อมีการแสดงข้อผิดพลาดหลายรายการพร้อมกัน
Now.on('form:errors', (data) => {
console.log('รายการข้อผิดพลาด:', data.errors);
});
// เมื่อแสดงข้อผิดพลาดระดับฟอร์ม
Now.on('form:generalError', (data) => {
console.log('ข้อผิดพลาดระดับฟอร์ม:', data.message);
console.log('คอนเทนเนอร์:', data.containerId);
});
// เมื่อเคลียร์ข้อผิดพลาดระดับฟอร์ม
Now.on('form:clearGeneralError', (data) => {
console.log('ล้างข้อผิดพลาดระดับฟอร์มแล้ว:', data.containerId);
});
// เมื่อแสดงข้อความสำเร็จ
Now.on('form:generalSuccess', (data) => {
console.log('ข้อความสำเร็จ:', data.message);
console.log('คอนเทนเนอร์:', data.containerId);
});
// เมื่อเคลียร์ข้อความสำเร็จ
Now.on('form:clearSuccess', (data) => {
console.log('ล้างข้อความสำเร็จแล้ว:', data.containerId);
});
// เมื่อมีการล้างข้อผิดพลาดทั้งหมด
Now.on('form:clearAllErrors', (data) => {
console.log('ล้างข้อผิดพลาดทั้งหมดแล้ว');
if (data.form) {
console.log('ฟอร์ม:', data.form);
}
});ตัวอย่างการใช้เหตุการณ์
// จัดเก็บข้อมูลข้อผิดพลาดเพื่อวิเคราะห์
Now.on('form:error', (data) => {
analytics.track('Form Error', {
field: data.field,
message: data.messages[0],
formId: data.element.form?.id
});
});
// แสดงการแจ้งเตือนแบบกำหนดเอง
Now.on('form:generalError', (data) => {
if (data.config.showErrorsInNotification) {
NotificationSystem.error(data.message);
}
});
// บันทึกเมื่อมีการล้างข้อผิดพลาดทั้งหมด
Now.on('form:clearAllErrors', () => {
console.log('ผู้ใช้ล้างข้อผิดพลาดของฟอร์มทั้งหมดแล้ว');
});การผสานรวมกับ FormManager
FormError สามารถทำงานร่วมกับ FormManager ได้อย่างราบรื่น:
การแสดงข้อผิดพลาดอัตโนมัติ
// FormManager จะเรียกใช้ FormError สำหรับข้อผิดพลาดการตรวจสอบโดยอัตโนมัติ
const form = document.getElementById('loginForm');
const formManager = FormManager.init(form);
// เมื่อการตรวจสอบล้มเหลว FormError จะแสดงข้อผิดพลาดให้อัตโนมัติ
formManager.validate(); // ข้อผิดพลาดจะแสดงผ่าน FormErrorการตั้งค่า Inheritance
<form
data-form="myForm"
data-error-class="has-error"
data-error-message-class="error-text"
data-auto-focus="true"
data-auto-scroll="false"
data-error-container="form-errors">
<!-- FormError จะใช้ค่าการตั้งหาเหล่านี้ -->
</form>การจัดการข้อผิดพลาดด้วยตนเอง
// แสดงข้อผิดพลาดที่กำหนดเองหลังส่งฟอร์ม
Now.on('form:submit', async (data) => {
try {
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(data.formData)
});
const result = await response.json();
if (!result.success && result.errors) {
// แสดงข้อผิดพลาดจากการตรวจสอบที่ส่งมาจากเซิร์ฟเวอร์
FormError.showErrors(result.errors);
}
} catch (error) {
FormError.showGeneralError('เกิดข้อผิดพลาดของเซิร์ฟเวอร์', data.form);
}
});ตัวอย่างที่สมบูรณ์
ตัวอย่างที่ 1: ฟอร์มเข้าสู่ระบบพร้อมการจัดการข้อผิดพลาด
<!DOCTYPE html>
<html>
<head>
<title>ฟอร์มเข้าสู่ระบบ</title>
<script src="/Now/Now.js"></script>
<style>
.form-control { margin-bottom: 1rem; }
.form-control.invalid input { border-color: #dc3545; }
.comment { min-height: 1.5rem; font-size: 0.875rem; }
.comment.error { color: #dc3545; }
.form-message { padding: 1rem; margin-bottom: 1rem; border-radius: 4px; }
.form-message.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.form-message.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
</style>
</head>
<body>
<form id="loginForm" data-form="loginForm">
<div id="form-message" class="form-message"></div>
<div class="form-control">
<label for="email">อีเมล</label>
<input type="email" id="email" name="email" required>
<div id="result_email" class="comment"></div>
</div>
<div class="form-control">
<label for="password">รหัสผ่าน</label>
<input type="password" id="password" name="password" required>
<div id="result_password" class="comment"></div>
</div>
<button type="submit">เข้าสู่ระบบ</button>
</form>
<script>
// กำหนดค่า FormError
FormError.configure({
errorClass: 'invalid',
autoFocus: true,
autoScroll: true,
autoClearErrors: false // ควรล้างข้อความเองสำหรับฟอร์มเข้าสู่ระบบ
});
// เริ่มต้นฟอร์ม
const form = document.getElementById('loginForm');
// จัดการเหตุการณ์เมื่อส่งฟอร์ม
form.addEventListener('submit', async (e) => {
e.preventDefault();
// ล้างข้อผิดพลาดเดิมทั้งหมดก่อน
FormError.clearAll(form);
// อ่านข้อมูลจากฟอร์ม
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// ตรวจสอบข้อมูลบนฝั่งไคลเอนต์
const errors = {};
if (!data.email) {
errors.email = 'ต้องระบุอีเมล';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.email = 'กรุณากรอกอีเมลที่ถูกต้อง';
}
if (!data.password) {
errors.password = 'ต้องระบุรหัสผ่าน';
} else if (data.password.length < 8) {
errors.password = 'รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร';
}
// แสดงข้อผิดพลาดหากมี
if (Object.keys(errors).length > 0) {
FormError.showErrors(errors);
FormError.showGeneralError('กรุณาแก้ไขข้อผิดพลาดด้านล่าง', form);
return;
}
// ส่งข้อมูลไปยังเซิร์ฟเวอร์
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
FormError.showSuccess('เข้าสู่ระบบสำเร็จ! กำลังเปลี่ยนเส้นทาง...', form);
setTimeout(() => {
window.location.href = result.redirect || '/dashboard';
}, 1500);
} else {
// แสดงข้อผิดพลาดที่ส่งมาจากเซิร์ฟเวอร์
if (result.errors) {
FormError.showErrors(result.errors);
}
FormError.showGeneralError(result.message || 'เข้าสู่ระบบล้มเหลว', form);
}
} catch (error) {
FormError.showGeneralError('เครือข่ายมีปัญหา กรุณาลองใหม่อีกครั้ง', form);
}
});
// ล้างข้อผิดพลาดของฟิลด์เมื่อผู้ใช้พิมพ์ข้อมูลใหม่
form.querySelectorAll('input').forEach(input => {
input.addEventListener('input', () => {
if (FormError.state.errors.has(input.name)) {
FormError.clearFieldError(input.name);
}
});
});
</script>
</body>
</html>ตัวอย่างที่ 2: ฟอร์มลงทะเบียนพร้อมการตรวจสอบหลายฟิลด์
<!DOCTYPE html>
<html>
<head>
<title>ฟอร์มลงทะเบียน</title>
<script src="/Now/Now.js"></script>
<style>
.form-control { margin-bottom: 1rem; }
.form-control.invalid input { border-color: #dc3545; background-color: #fff5f5; }
.comment { min-height: 1.5rem; font-size: 0.875rem; }
.comment.error { color: #dc3545; }
.form-message { padding: 1rem; margin-bottom: 1rem; border-radius: 4px; display: none; }
.form-message.show { display: block; }
.form-message.error { background: #f8d7da; color: #721c24; }
.form-message.success { background: #d4edda; color: #155724; }
</style>
</head>
<body>
<form id="registrationForm" data-form="registrationForm">
<div id="form-message" class="form-message"></div>
<div class="form-control">
<label for="username">ชื่อผู้ใช้</label>
<input type="text" id="username" name="username" required>
<div id="result_username" class="comment"></div>
</div>
<div class="form-control">
<label for="email">อีเมล</label>
<input type="email" id="email" name="email" required>
<div id="result_email" class="comment"></div>
</div>
<div class="form-control">
<label for="password">รหัสผ่าน</label>
<input type="password" id="password" name="password" required>
<div id="result_password" class="comment"></div>
</div>
<div class="form-control">
<label for="confirmPassword">ยืนยันรหัสผ่าน</label>
<input type="password" id="confirmPassword" name="confirmPassword" required>
<div id="result_confirmPassword" class="comment"></div>
</div>
<div class="form-control">
<label>
<input type="checkbox" id="terms" name="terms" required>
ฉันยอมรับข้อกำหนดและเงื่อนไข
</label>
<div id="result_terms" class="comment"></div>
</div>
<button type="submit">ลงทะเบียน</button>
</form>
<script>
// กำหนดค่า FormError
FormError.configure({
errorClass: 'invalid',
errorMessageClass: 'error',
autoFocus: true,
autoScroll: true,
scrollOffset: 80,
autoClearErrors: false
});
const form = document.getElementById('registrationForm');
// กฎการตรวจสอบข้อมูล
const validationRules = {
username: {
validate: (value) => {
if (!value) return 'ต้องระบุชื่อผู้ใช้';
if (value.length < 4) return 'ชื่อผู้ใช้ต้องมีอย่างน้อย 4 ตัวอักษร';
if (!/^[a-zA-Z0-9_]+$/.test(value)) return 'ชื่อผู้ใช้ต้องเป็นตัวอักษร ตัวเลข หรือขีดล่างเท่านั้น';
return null;
}
},
email: {
validate: (value) => {
if (!value) return 'ต้องระบุอีเมล';
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'กรุณากรอกอีเมลที่ถูกต้อง';
return null;
}
},
password: {
validate: (value) => {
if (!value) return 'ต้องระบุรหัสผ่าน';
if (value.length < 8) return 'รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร';
if (!/[A-Z]/.test(value)) return 'รหัสผ่านต้องมีตัวพิมพ์ใหญ่อย่างน้อย 1 ตัว';
if (!/[a-z]/.test(value)) return 'รหัสผ่านต้องมีตัวพิมพ์เล็กอย่างน้อย 1 ตัว';
if (!/[0-9]/.test(value)) return 'รหัสผ่านต้องมีตัวเลขอย่างน้อย 1 ตัว';
return null;
}
},
confirmPassword: {
validate: (value, formData) => {
if (!value) return 'กรุณายืนยันรหัสผ่านของคุณ';
if (value !== formData.password) return 'รหัสผ่านไม่ตรงกัน';
return null;
}
},
terms: {
validate: (value) => {
if (!value) return 'คุณต้องยอมรับข้อกำหนดและเงื่อนไขก่อน';
return null;
}
}
};
// การตรวจสอบแบบเรียลไทม์เมื่อผู้ใช้เลื่อนออกจากฟิลด์
form.querySelectorAll('input').forEach(input => {
input.addEventListener('blur', () => {
validateField(input.name);
});
// ล้างข้อความผิดพลาดเมื่อมีการกรอกข้อมูลใหม่
input.addEventListener('input', () => {
if (FormError.state.errors.has(input.name)) {
FormError.clearFieldError(input.name);
}
});
});
// ตรวจสอบฟิลด์เดี่ยว
function validateField(fieldName) {
const rule = validationRules[fieldName];
if (!rule) return true;
const formData = new FormData(form);
const data = Object.fromEntries(formData);
const value = data[fieldName];
const error = rule.validate(value, data);
if (error) {
FormError.showFieldError(fieldName, error, form, {
focus: false,
scroll: false
});
return false;
} else {
FormError.clearFieldError(fieldName);
return true;
}
}
// ตรวจสอบทุกฟิลด์ในคราวเดียว
function validateForm() {
const formData = new FormData(form);
const data = Object.fromEntries(formData);
const errors = {};
Object.keys(validationRules).forEach(fieldName => {
const rule = validationRules[fieldName];
const value = data[fieldName];
const error = rule.validate(value, data);
if (error) {
errors[fieldName] = error;
}
});
return errors;
}
// จัดการเหตุการณ์เมื่อส่งฟอร์ม
form.addEventListener('submit', async (e) => {
e.preventDefault();
// ล้างข้อผิดพลาดเดิมทั้งหมดก่อน
FormError.clearAll(form);
// ตรวจสอบข้อมูลทุกฟิลด์
const errors = validateForm();
if (Object.keys(errors).length > 0) {
FormError.showErrors(errors);
FormError.showGeneralError(`กรุณาแก้ไขข้อผิดพลาดจำนวน ${Object.keys(errors).length} รายการด้านล่าง`, form);
return;
}
// ส่งข้อมูลไปยังเซิร์ฟเวอร์
const formData = new FormData(form);
const data = Object.fromEntries(formData);
try {
const response = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
FormError.showSuccess('ลงทะเบียนสำเร็จ! กรุณาตรวจสอบอีเมลของคุณ', form);
form.reset();
setTimeout(() => {
window.location.href = '/login';
}, 2000);
} else {
// แสดงข้อผิดพลาดจากการตรวจสอบที่ส่งกลับจากเซิร์ฟเวอร์
if (result.errors) {
FormError.showErrors(result.errors);
}
FormError.showGeneralError(result.message || 'การลงทะเบียนไม่สำเร็จ', form);
}
} catch (error) {
FormError.showGeneralError('เครือข่ายมีปัญหา กรุณาลองใหม่อีกครั้ง', form);
}
});
</script>
</body>
</html>ตัวอย่างที่ 3: ฟอร์มติดต่อพร้อมล้างข้อความอัตโนมัติ
<!DOCTYPE html>
<html>
<head>
<title>ฟอร์มติดต่อ</title>
<script src="/Now/Now.js"></script>
<style>
.form-control { margin-bottom: 1rem; }
.form-control.invalid input,
.form-control.invalid textarea { border-color: #dc3545; }
.comment { min-height: 1.5rem; font-size: 0.875rem; }
.comment.error { color: #dc3545; }
#form-message {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
display: none;
animation: slideDown 0.3s ease-out;
}
#form-message.show { display: block; }
#form-message.error { background: #f8d7da; color: #721c24; }
#form-message.success { background: #d4edda; color: #155724; }
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<form id="contactForm" data-form="contactForm">
<div id="form-message" class="form-message"></div>
<div class="form-control">
<label for="name">ชื่อ</label>
<input type="text" id="name" name="name" required>
<div id="result_name" class="comment"></div>
</div>
<div class="form-control">
<label for="email">อีเมล</label>
<input type="email" id="email" name="email" required>
<div id="result_email" class="comment"></div>
</div>
<div class="form-control">
<label for="subject">หัวข้อ</label>
<input type="text" id="subject" name="subject" required>
<div id="result_subject" class="comment"></div>
</div>
<div class="form-control">
<label for="message">ข้อความ</label>
<textarea id="message" name="message" rows="5" required></textarea>
<div id="result_message" class="comment"></div>
</div>
<button type="submit">ส่งข้อความ</button>
</form>
<script>
// กำหนดค่าพร้อมการล้างอัตโนมัติ
FormError.configure({
errorClass: 'invalid',
errorMessageClass: 'error',
autoFocus: true,
autoScroll: true,
autoClearErrors: true,
autoClearErrorsDelay: 4000 // ล้างข้อความภายใน 4 วินาที
});
const form = document.getElementById('contactForm');
// เพิ่มคลาส 'show' เมื่อมีการแสดงข้อความ
Now.on('form:generalError', (data) => {
document.getElementById(data.containerId).classList.add('show');
});
Now.on('form:generalSuccess', (data) => {
document.getElementById(data.containerId).classList.add('show');
});
// จัดการการส่งฟอร์ม
form.addEventListener('submit', async (e) => {
e.preventDefault();
// ล้างข้อความก่อนหน้า
FormError.clearAll(form);
// อ่านข้อมูลจากฟอร์ม
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// ตรวจสอบแบบง่าย
const errors = {};
if (!data.name) errors.name = 'ต้องระบุชื่อ';
if (!data.email) errors.email = 'ต้องระบุอีเมล';
if (!data.subject) errors.subject = 'ต้องระบุหัวข้อ';
if (!data.message) errors.message = 'ต้องระบุข้อความ';
if (Object.keys(errors).length > 0) {
FormError.showErrors(errors);
FormError.showGeneralError('กรุณากรอกข้อมูลที่จำเป็นให้ครบถ้วน', form);
return;
}
// จำลองการเรียก API
try {
// แสดงสถานะกำลังประมวลผล
const submitBtn = form.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = 'กำลังส่ง...';
await new Promise(resolve => setTimeout(resolve, 1500));
// จำลองสถานการณ์สำเร็จ
FormError.showSuccess('ขอบคุณ! ส่งข้อความของคุณเรียบร้อยแล้ว', form);
form.reset();
// ข้อความจะถูกล้างอัตโนมัติภายใน 4 วินาที (autoClearErrorsDelay)
// รีเซ็ตสถานะปุ่ม
submitBtn.disabled = false;
submitBtn.textContent = 'ส่งข้อความ';
} catch (error) {
FormError.showGeneralError('ส่งข้อความไม่สำเร็จ กรุณาลองอีกครั้ง', form);
// รีเซ็ตสถานะปุ่ม
const submitBtn = form.querySelector('button[type="submit"]');
submitBtn.disabled = false;
submitBtn.textContent = 'ส่งข้อความ';
}
});
// ล้างข้อผิดพลาดของฟิลด์เมื่อมีการพิมพ์ข้อมูล
form.querySelectorAll('input, textarea').forEach(field => {
field.addEventListener('input', () => {
if (FormError.state.errors.has(field.name)) {
FormError.clearFieldError(field.name);
}
});
});
</script>
</body>
</html>ตัวอย่างที่ 4: ฟอร์มหลายขั้นตอนพร้อมการเก็บข้อผิดพลาด
<!DOCTYPE html>
<html>
<head>
<title>ฟอร์มหลายขั้นตอน</title>
<script src="/Now/Now.js"></script>
<style>
.form-step { display: none; }
.form-step.active { display: block; }
.form-control { margin-bottom: 1rem; }
.form-control.invalid input { border-color: #dc3545; }
.comment { min-height: 1.5rem; font-size: 0.875rem; }
.comment.error { color: #dc3545; }
.form-message { padding: 1rem; margin-bottom: 1rem; border-radius: 4px; }
.form-message.error { background: #f8d7da; color: #721c24; }
.form-message.success { background: #d4edda; color: #155724; }
.form-nav { margin-top: 1rem; display: flex; gap: 1rem; }
.step-indicator { display: flex; gap: 1rem; margin-bottom: 1rem; }
.step-indicator span {
padding: 0.5rem 1rem;
background: #e9ecef;
border-radius: 4px;
}
.step-indicator span.active { background: #007bff; color: white; }
.step-indicator span.completed { background: #28a745; color: white; }
</style>
</head>
<body>
<div class="step-indicator">
<span class="active" data-step="1">ขั้นตอนที่ 1: ข้อมูลส่วนตัว</span>
<span data-step="2">ขั้นตอนที่ 2: ที่อยู่</span>
<span data-step="3">ขั้นตอนที่ 3: ยืนยัน</span>
</div>
<form id="multiStepForm" data-form="multiStepForm">
<div id="form-message" class="form-message"></div>
<!-- ขั้นตอนที่ 1: ข้อมูลส่วนตัว -->
<div class="form-step active" data-step="1">
<h3>ข้อมูลส่วนตัว</h3>
<div class="form-control">
<label for="firstName">ชื่อจริง</label>
<input type="text" id="firstName" name="firstName" required>
<div id="result_firstName" class="comment"></div>
</div>
<div class="form-control">
<label for="lastName">นามสกุล</label>
<input type="text" id="lastName" name="lastName" required>
<div id="result_lastName" class="comment"></div>
</div>
<div class="form-control">
<label for="email">อีเมล</label>
<input type="email" id="email" name="email" required>
<div id="result_email" class="comment"></div>
</div>
</div>
<!-- ขั้นตอนที่ 2: ที่อยู่ -->
<div class="form-step" data-step="2">
<h3>ข้อมูลที่อยู่</h3>
<div class="form-control">
<label for="street">ที่อยู่</label>
<input type="text" id="street" name="street" required>
<div id="result_street" class="comment"></div>
</div>
<div class="form-control">
<label for="city">เมือง</label>
<input type="text" id="city" name="city" required>
<div id="result_city" class="comment"></div>
</div>
<div class="form-control">
<label for="zipcode">รหัสไปรษณีย์</label>
<input type="text" id="zipcode" name="zipcode" required>
<div id="result_zipcode" class="comment"></div>
</div>
</div>
<!-- ขั้นตอนที่ 3: ยืนยัน -->
<div class="form-step" data-step="3">
<h3>การยืนยัน</h3>
<div id="confirmationData"></div>
</div>
<div class="form-nav">
<button type="button" id="prevBtn" style="display: none;">ย้อนกลับ</button>
<button type="button" id="nextBtn">ถัดไป</button>
<button type="submit" id="submitBtn" style="display: none;">ส่งข้อมูล</button>
</div>
</form>
<script>
FormError.configure({
errorClass: 'invalid',
autoFocus: true,
autoScroll: false, // Disable for multi-step
autoClearErrors: false
});
const form = document.getElementById('multiStepForm');
let currentStep = 1;
const totalSteps = 3;
const stepErrors = {}; // เก็บข้อผิดพลาดของแต่ละขั้นตอน
// กำหนดฟิลด์ที่ตรวจสอบในแต่ละขั้นตอน
const stepFields = {
1: ['firstName', 'lastName', 'email'],
2: ['street', 'city', 'zipcode']
};
// อัปเดตส่วนแสดงผลของขั้นตอน
function updateStepUI() {
// ปรับการแสดงผลของแต่ละขั้นตอน
document.querySelectorAll('.form-step').forEach(step => {
step.classList.remove('active');
});
document.querySelector(`.form-step[data-step="${currentStep}"]`).classList.add('active');
// ปรับตัวบ่งชี้ขั้นตอน
document.querySelectorAll('.step-indicator span').forEach((span, index) => {
const stepNum = index + 1;
span.classList.remove('active', 'completed');
if (stepNum < currentStep) {
span.classList.add('completed');
} else if (stepNum === currentStep) {
span.classList.add('active');
}
});
// ปรับสถานะปุ่มนำทาง
document.getElementById('prevBtn').style.display = currentStep > 1 ? 'block' : 'none';
document.getElementById('nextBtn').style.display = currentStep < totalSteps ? 'block' : 'none';
document.getElementById('submitBtn').style.display = currentStep === totalSteps ? 'block' : 'none';
// แสดงข้อมูลยืนยันในขั้นตอนสุดท้าย
if (currentStep === 3) {
showConfirmation();
}
// ล้างข้อความระดับฟอร์มเมื่อเปลี่ยนขั้นตอน
FormError.clearGeneralError(form);
}
// ตรวจสอบข้อมูลของขั้นตอนปัจจุบัน
function validateCurrentStep() {
const fields = stepFields[currentStep];
if (!fields) return true; // No validation for confirmation step
const formData = new FormData(form);
const data = Object.fromEntries(formData);
const errors = {};
fields.forEach(field => {
const value = data[field];
if (!value) {
errors[field] = 'จำเป็นต้องกรอกข้อมูลในช่องนี้';
}
});
// การตรวจสอบเพิ่มเติม
if (currentStep === 1 && data.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.email = 'กรุณากรอกอีเมลที่ถูกต้อง';
}
if (currentStep === 2 && data.zipcode && !/^\d{5}$/.test(data.zipcode)) {
errors.zipcode = 'รหัสไปรษณีย์ต้องเป็นตัวเลข 5 หลัก';
}
// ล้างข้อผิดพลาดของขั้นตอนก่อนหน้า
FormError.clearFormMessages(form);
if (Object.keys(errors).length > 0) {
stepErrors[currentStep] = errors;
FormError.showErrors(errors);
FormError.showGeneralError('กรุณาแก้ไขข้อผิดพลาดก่อนดำเนินการต่อ', form);
return false;
} else {
delete stepErrors[currentStep];
return true;
}
}
// แสดงข้อมูลสรุปก่อนยืนยัน
function showConfirmation() {
const formData = new FormData(form);
const data = Object.fromEntries(formData);
const html = `
<h4>ข้อมูลส่วนตัว</h4>
<p><strong>ชื่อ:</strong> ${data.firstName} ${data.lastName}</p>
<p><strong>อีเมล:</strong> ${data.email}</p>
<h4>ที่อยู่</h4>
<p><strong>ที่อยู่:</strong> ${data.street}</p>
<p><strong>เมือง:</strong> ${data.city}</p>
<p><strong>รหัสไปรษณีย์:</strong> ${data.zipcode}</p>
`;
document.getElementById('confirmationData').innerHTML = html;
}
// ปุ่มถัดไป
document.getElementById('nextBtn').addEventListener('click', () => {
if (validateCurrentStep()) {
currentStep++;
updateStepUI();
}
});
// ปุ่มย้อนกลับ
document.getElementById('prevBtn').addEventListener('click', () => {
currentStep--;
updateStepUI();
// หากมีข้อผิดพลาดของขั้นตอนก่อนหน้าให้แสดงกลับมา
if (stepErrors[currentStep]) {
FormError.showErrors(stepErrors[currentStep]);
}
});
// เมื่อส่งฟอร์ม
form.addEventListener('submit', async (e) => {
e.preventDefault();
FormError.clearAll(form);
const formData = new FormData(form);
const data = Object.fromEntries(formData);
try {
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = true;
submitBtn.textContent = 'กำลังส่ง...';
// จำลองการเรียก API
await new Promise(resolve => setTimeout(resolve, 1500));
FormError.showSuccess('ลงทะเบียนเรียบร้อยแล้ว!', form);
setTimeout(() => {
window.location.href = '/dashboard';
}, 2000);
} catch (error) {
FormError.showGeneralError('ส่งข้อมูลไม่สำเร็จ กรุณาลองใหม่อีกครั้ง', form);
document.getElementById('submitBtn').disabled = false;
document.getElementById('submitBtn').textContent = 'ส่งข้อมูล';
}
});
// ล้างข้อผิดพลาดของฟิลด์เมื่อมีการพิมพ์ข้อมูลใหม่
form.querySelectorAll('input').forEach(input => {
input.addEventListener('input', () => {
if (FormError.state.errors.has(input.name)) {
FormError.clearFieldError(input.name);
}
});
});
// เริ่มต้นการทำงาน
updateStepUI();
</script>
</body>
</html>เอกสารอ้างอิง API
การตั้งค่า
FormError.configure(options: {
errorClass?: string; // คลาส CSS สำหรับฟิลด์ที่ไม่ผ่านการตรวจสอบ (ค่าเริ่มต้น: 'invalid')
errorMessageClass?: string; // คลาส CSS สำหรับข้อความข้อผิดพลาด (ค่าเริ่มต้น: 'error')
autoFocus?: boolean; // โฟกัสฟิลด์ที่มีข้อผิดพลาดตัวแรกโดยอัตโนมัติ (ค่าเริ่มต้น: true)
autoScroll?: boolean; // เลื่อนอัตโนมัติไปยังข้อผิดพลาดตัวแรก (ค่าเริ่มต้น: true)
scrollOffset?: number; // ระยะเลื่อนในหน่วยพิกเซล (ค่าเริ่มต้น: 100)
debug?: boolean; // เปิดการบันทึกข้อมูลดีบัก (ค่าเริ่มต้น: false)
showErrorsInline?: boolean; // แสดงข้อความผิดพลาดถัดจากฟิลด์โดยตรง (ค่าเริ่มต้น: true)
showErrorsInNotification?: boolean; // แสดงผ่านระบบแจ้งเตือน (ค่าเริ่มต้น: false)
autoClearErrors?: boolean; // ล้างข้อความอัตโนมัติ (ค่าเริ่มต้น: true)
autoClearErrorsDelay?: number; // ระยะเวลาก่อนล้างอัตโนมัติ (มิลลิวินาที) (ค่าเริ่มต้น: 5000)
defaultErrorContainer?: string; // ID ของ container ข้อผิดพลาดเริ่มต้น (ค่าเริ่มต้น: 'form-message')
defaultSuccessContainer?: string; // ID ของ container ข้อความสำเร็จเริ่มต้น (ค่าเริ่มต้น: 'form-message')
}): voidข้อผิดพลาดของฟิลด์
// แสดงข้อผิดพลาดฟิลด์
FormError.showFieldError(
field: string, // ID หรือชื่อฟิลด์
message: string | string[], // ข้อความข้อผิดพลาด (หนึ่งหรือหลายข้อความ)
form?: HTMLElement | null, // องค์ประกอบฟอร์ม (ไม่จำเป็น)
options?: {
focus?: boolean; // กำหนดพฤติกรรมการโฟกัส
scroll?: boolean; // กำหนดพฤติกรรมการเลื่อนหน้าจอ
}
): void
// ล้างข้อผิดพลาดของฟิลด์
FormError.clearFieldError(
field: string | HTMLElement // ID, ชื่อ หรือองค์ประกอบของฟิลด์
): void
// แสดงข้อผิดพลาดหลายฟิลด์พร้อมกัน
FormError.showErrors(
errors: { [field: string]: string | string[] },
options?: {
focus?: boolean; // ปรับการโฟกัส
scroll?: boolean; // ปรับการเลื่อนหน้าจอ
}
): voidข้อความทั่วไป
// แสดงข้อผิดพลาดทั่วไป
FormError.showGeneralError(
message: string, // ข้อความข้อผิดพลาดระดับฟอร์ม
form?: HTMLElement | string | null, // องค์ประกอบฟอร์มหรือ ID ของ container
options?: {} // สำรองไว้สำหรับการใช้งานในอนาคต
): void
// แสดงข้อความสำเร็จ
FormError.showSuccess(
message: string, // ข้อความสำเร็จ
form?: HTMLElement | string | null, // องค์ประกอบฟอร์มหรือ ID ของ container
options?: {} // สำรองไว้สำหรับฟีเจอร์ในอนาคต
): void
// ล้างข้อความข้อผิดพลาดระดับฟอร์ม
FormError.clearGeneralError(
containerOrForm?: string | HTMLElement | null // ID ของ container หรือองค์ประกอบฟอร์ม
): boolean
// ล้างข้อความสำเร็จ
FormError.clearSuccess(
containerOrForm?: string | HTMLElement | null // ID ของ container หรือองค์ประกอบฟอร์ม
): booleanการดำเนินการเป็นกลุ่ม
// ล้างข้อความทั้งหมด
FormError.clearAll(
form?: HTMLElement | null // ระบุฟอร์มที่ต้องการล้าง (ว่างได้)
): void
// ล้างข้อความเฉพาะฟอร์มที่ระบุ
FormError.clearFormMessages(
form: HTMLElement | string // องค์ประกอบฟอร์มหรือ ID ของฟอร์ม
): booleanเครื่องมือช่วยเหลือ
// เลื่อนไปที่องค์ประกอบ
FormError.scrollToElement(
element: HTMLElement // องค์ประกอบที่ต้องการเลื่อนไปหา
): void
// ตรวจสอบว่ามีข้อผิดพลาดอยู่หรือไม่
FormError.hasErrors(): boolean
// รับรายการข้อผิดพลาดทั้งหมด
FormError.getErrors(): Array<{
field: string;
element: HTMLElement;
messages: string[];
}>
// ตรวจนับจำนวนข้อผิดพลาดทั้งหมด
FormError.getErrorsCount(): number
// รีเซ็ตสถานะทั้งหมดของ FormError
FormError.reset(): void
// รับการตั้งค่าเฉพาะของฟอร์มที่กำหนด
FormError.getFormConfig(
form: HTMLElement // องค์ประกอบฟอร์มที่ต้องการตรวจสอบ
): ConfigObjectคุณสมบัติสถานะ
FormError.state = {
errors: Map<string, { // รายการข้อผิดพลาดปัจจุบัน
element: HTMLElement;
messages: string[];
}>,
lastFocusedElement: HTMLElement | null, // องค์ประกอบที่ถูกโฟกัสล่าสุดโดยอัตโนมัติ
originalMessages: Map<string, string>, // ข้อความเดิมของฟิลด์แต่ละตัว
originalGeneralMessages: Map<string, { // เนื้อหาเดิมของ container ระดับฟอร์ม
html: string;
className: string;
}>
}
FormError.config = {
// ค่าการตั้งค่าปัจจุบัน (ดูรายละเอียดในหัวข้อการตั้งค่า)
}แนวทางปฏิบัติที่ดี
1. ใช้โครงสร้าง HTML ที่เหมาะสม
<!-- ตัวอย่างที่ถูกต้อง: โครงสร้างเหมาะกับการแสดงข้อผิดพลาด -->
<div class="form-control">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<div id="result_email" class="comment"></div>
</div>
<!-- ตัวอย่างที่ไม่ถูกต้อง: ไม่มี container สำหรับข้อผิดพลาด -->
<div class="form-control">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<!-- ไม่มี container สำหรับข้อผิดพลาด -->
</div>2. ล้างข้อผิดพลาดอย่างเหมาะสม
// ดี: ล้างข้อผิดพลาดเมื่อผู้ใช้เริ่มพิมพ์
input.addEventListener('input', () => {
FormError.clearFieldError(input.name);
});
// ดี: ล้างข้อผิดพลาดทั้งหมดก่อนตรวจสอบรอบใหม่
FormError.clearAll(form);
const errors = validateForm();
if (errors) {
FormError.showErrors(errors);
}
// ไม่ดี: ไม่ล้างข้อผิดพลาดเดิมก่อนแสดงข้อมูลใหม่
FormError.showFieldError('email', 'ข้อผิดพลาด 1');
FormError.showFieldError('email', 'ข้อผิดพลาด 2'); // จะถูกเขียนทับ แต่ควรล้างก่อน3. ใช้เหตุการณ์สำหรับการผสานรวม
// ดี: รับฟังอีเวนต์ของ FormError เพื่อต่อยอดพฤติกรรมเอง
Now.on('form:error', (data) => {
// บันทึกข้อผิดพลาดเพื่อใช้งานด้านการวิเคราะห์
analytics.track('Validation Error', {
field: data.field,
message: data.messages[0]
});
});
// ดี: ผสานเข้ากับระบบแจ้งเตือนที่มีอยู่
Now.on('form:generalError', (data) => {
if (data.config.showErrorsInNotification) {
showToast(data.message, 'error');
}
});4. ตั้งค่าตามกรณีการใช้งาน
// ฟอร์มเข้าสู่ระบบ: ไม่ต้องล้างข้อผิดพลาดอัตโนมัติ
FormError.configure({
autoClearErrors: false
});
// ฟอร์มติดต่อ: ล้างอัตโนมัติหลังหน่วงเวลา
FormError.configure({
autoClearErrors: true,
autoClearErrorsDelay: 4000
});
// ฟอร์มลงทะเบียน: ปิดการเลื่อนอัตโนมัติสำหรับฟอร์มยาว
FormError.configure({
autoScroll: false
});5. รักษาประสบการณ์ผู้ใช้
// ดี: แสดงข้อผิดพลาดและโฟกัสที่ฟิลด์แรก
FormError.showErrors(errors); // ฟิลด์แรกจะถูกโฟกัสให้
// ดี: ไม่แย่งโฟกัสในระหว่างตรวจสอบเมื่อออกจากฟิลด์
FormError.showFieldError('email', 'Invalid', form, {
focus: false,
scroll: false
});
// ไม่ดี: แย่งโฟกัสระหว่างผู้ใช้กำลังพิมพ์
input.addEventListener('input', () => {
FormError.showFieldError(input.name, 'Invalid', form); // Steals focus
});6. จัดการข้อผิดพลาดของเซิร์ฟเวอร์อย่างเหมาะสม
// ดี: แม็ปข้อผิดพลาดจากเซิร์ฟเวอร์ไปยังฟิลด์ที่เกี่ยวข้อง
try {
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(formData)
});
const result = await response.json();
if (!result.success) {
// แสดงข้อผิดพลาดในแต่ละฟิลด์
if (result.errors) {
FormError.showErrors(result.errors);
}
// แสดงข้อผิดพลาดระดับฟอร์ม
FormError.showGeneralError(result.message || 'ส่งข้อมูลไม่สำเร็จ', form);
}
} catch (error) {
// Network error
FormError.showGeneralError('เครือข่ายมีปัญหา กรุณาลองใหม่อีกครั้ง', form);
}7. การปฏิบัติตามมาตรฐานการเข้าถึง
// ดี: FormError จัดการ ARIA attributes ให้อัตโนมัติ
FormError.showFieldError('email', 'อีเมลไม่ถูกต้อง');
// ผลลัพธ์: aria-invalid="true" และ aria-errormessage="result_email"
// ดี: ใช้ HTML ที่มีความหมายชัดเจน
<div id="result_email" role="alert">ต้องระบุอีเมล</div>
// ดี: ดูแลให้ผู้ใช้แป้นพิมพ์ใช้งานได้สะดวก
FormError.configure({
autoFocus: true // First error field receives focus
});8. การจัดการข้อผิดพลาดฟอร์มหลายขั้นตอน
// ดี: บันทึกข้อผิดพลาดของแต่ละขั้นตอน
const stepErrors = {};
function validateStep(stepNumber) {
const errors = validateStepFields(stepNumber);
if (Object.keys(errors).length > 0) {
stepErrors[stepNumber] = errors;
FormError.showErrors(errors);
return false;
} else {
delete stepErrors[stepNumber];
return true;
}
}
// ดี: แสดงข้อผิดพลาดเดิมเมื่อย้อนกลับไป
function goToPreviousStep() {
currentStep--;
if (stepErrors[currentStep]) {
FormError.showErrors(stepErrors[currentStep]);
}
}ข้อผิดพลาดที่พบบ่อย
1. ขาด Container ข้อผิดพลาด
ปัญหา:
<!-- ไม่มี container result_ -->
<input type="email" id="email" name="email">วิธีแก้:
<input type="email" id="email" name="email">
<div id="result_email" class="comment"></div>2. ตัวระบุฟิลด์ไม่ถูกต้อง
ปัญหา:
// ฟิลด์มี id="userEmail" แต่พยายามแสดงข้อผิดพลาดให้กับ "email"
FormError.showFieldError('email', 'อีเมลไม่ถูกต้อง'); // ใช้งานไม่ได้วิธีแก้:
// Use correct field ID or name
FormError.showFieldError('userEmail', 'อีเมลไม่ถูกต้อง');3. ไม่ล้างข้อผิดพลาดก่อนตรวจสอบ
ปัญหา:
// Old errors still visible
FormError.showErrors(newErrors); // Mixed with old errorsวิธีแก้:
// ต้องล้างข้อมูลก่อนทุกครั้งก่อนแสดงข้อผิดพลาดใหม่
FormError.clearAll(form);
FormError.showErrors(newErrors);4. การแย่งชิงโฟกัสระหว่างพิมพ์
ปัญหา:
// Steals focus while user is typing
input.addEventListener('input', () => {
FormError.showFieldError(input.name, 'ข้อผิดพลาด', form); // autoFocus: true (ค่าเริ่มต้น)
});วิธีแก้:
// Disable focus for real-time validation
input.addEventListener('input', () => {
FormError.showFieldError(input.name, 'ข้อผิดพลาด', form, {
focus: false,
scroll: false
});
});5. ไม่จัดการเนื้อหาเดิม
ปัญหา:
// Manually clearing containers loses original content
container.textContent = '';วิธีแก้:
// Use FormError methods to preserve/restore content
FormError.clearGeneralError(container);
// เนื้อหาเดิมจะถูกคืนค่าให้อัตโนมัติ6. ลืมบริบทของฟอร์ม
ปัญหา:
// ไม่ได้ใช้งานการตั้งค่าเฉพาะฟอร์ม
FormError.showGeneralError('เกิดข้อผิดพลาด'); // ใช้ container ค่าเริ่มต้นวิธีแก้:
// ส่งองค์ประกอบฟอร์มเข้าไปเพื่อใช้การตั้งค่าของฟอร์ม
const form = document.getElementById('myForm');
FormError.showGeneralError('เกิดข้อผิดพลาด', form); // ใช้ container ที่กำหนดในฟอร์ม7. ไม่จัดการเวลาล้างอัตโนมัติ
ปัญหา:
// ฟอร์มถูกรีเซ็ตก่อนที่จะล้างอัตโนมัติ
FormError.showSuccess('Saved!', form); // ล้างอัตโนมัติหลังจาก 5 วินาที
setTimeout(() => {
window.location.href = '/dashboard'; // นำทางหลังจาก 1 วินาที
}, 1000); // User never sees full 5 secondsวิธีแก้:
// Coordinate timing or disable auto-clear
FormError.configure({ autoClearErrors: false });
FormError.showSuccess('Saved! Redirecting...', form);
setTimeout(() => {
window.location.href = '/dashboard';
}, 2000); // Give user time to read message8. รูปแบบข้อผิดพลาดจากเซิร์ฟเวอร์ไม่ถูกต้อง
ปัญหา:
// เซิร์ฟเวอร์ตอบกลับ: { error: "Login failed" }
// แต่ FormError คาดหวังรูปแบบ: { fieldName: "message" }วิธีแก้:
const result = await response.json();
if (!result.success) {
// Handle general error
if (result.error) {
FormError.showGeneralError(result.error, form);
}
// Handle field errors
if (result.errors) {
FormError.showErrors(result.errors);
}
}ความเข้ากันได้ของเบราว์เซอร์
- Modern Browsers: รองรับอย่างเต็มรูปแบบ (Chrome, Firefox, Safari, Edge)
- IE11 : ต้องใช้ polyfills สำหรับ Map, Object.entries
- Mobile: รองรับ iOS Safari, Chrome Android อย่างเต็มรูปแบบ
เอกสารที่เกี่ยวข้อง
- FormManager-ระบบการจัดการและการตรวจสอบฟอร์ม
- FormValidation - กฎการตรวจสอบและตัวตรวจสอบที่กำหนดเอง
- ResponseHandler - การจัดการการตอบสนองของ API
- ElementManager - การจัดการสถานะขององค์ประกอบ UI
คู่มือการโยกย้าย
จากการจัดการข้อผิดพลาดด้วยตนเอง
Before:
// Manual error display
document.getElementById('email').classList.add('invalid');
document.getElementById('result_email').textContent = 'ต้องระบุอีเมล';
document.getElementById('email').focus();After:
// Using FormError
FormError.showFieldError('email', 'ต้องระบุอีเมล');
// ระบบจะเพิ่มคลาส แสดงข้อความ โฟกัส และเลื่อนให้อัตโนมัติจากตัวจัดการข้อผิดพลาดที่กำหนดเอง
Before:
myErrorManager.showError({
field: 'email',
message: 'ไม่ถูกต้อง',
focus: true
});After:
FormError.showFieldError('email', 'Invalid');
// ลักษณะการทำงานเดียวกันกับ FormError API