การเข้าร่วมแบบฟอร์มจะง่ายขึ้นมากด้วยเหตุการณ์ใหม่และ API องค์ประกอบที่กำหนดเอง
นักพัฒนาซอฟต์แวร์จำนวนมากสร้างการควบคุมแบบฟอร์มที่กำหนดเอง เพื่อเป็นการควบคุมที่ไม่มีอยู่แล้วในเบราว์เซอร์ หรือเพื่อปรับแต่งรูปลักษณ์และความรู้สึกเองมากกว่าที่สามารถทำได้ด้วยตัวควบคุมฟอร์มในตัว
แต่การสร้างฟีเจอร์ของตัวควบคุมแบบฟอร์ม HTML ที่มีอยู่แล้วอาจทำได้ยาก พิจารณาฟีเจอร์บางอย่างที่องค์ประกอบ <input>
จะได้รับโดยอัตโนมัติเมื่อคุณเพิ่มลงในแบบฟอร์ม
- ระบบจะเพิ่มอินพุตลงในรายการตัวควบคุมของแบบฟอร์มโดยอัตโนมัติ
- ระบบจะส่งค่าของอินพุตไปกับแบบฟอร์มโดยอัตโนมัติ
- อินพุตจะมีส่วนร่วมในการตรวจสอบแบบฟอร์ม คุณจัดรูปแบบอินพุตได้โดยใช้ Pseudoclasses
:valid
และ:invalid
- ระบบจะแจ้งเตือนอินพุตเมื่อมีการรีเซ็ตแบบฟอร์ม เมื่อมีการโหลดแบบฟอร์มซ้ำ หรือเมื่อเบราว์เซอร์พยายามป้อนข้อมูลในแบบฟอร์มโดยอัตโนมัติ
การควบคุมแบบฟอร์มที่กำหนดเองมักจะมีฟีเจอร์เหล่านี้เพียงไม่กี่ข้อ นักพัฒนาซอฟต์แวร์สามารถหลีกเลี่ยงข้อจำกัดบางประการใน JavaScript เช่น การเพิ่ม <input>
ที่ซ่อนไว้ในแบบฟอร์มเพื่อเข้าร่วมการส่งแบบฟอร์ม แต่ฟีเจอร์อื่นๆ ไม่สามารถคัดลอกได้ใน JavaScript เพียงอย่างเดียว
ฟีเจอร์ใหม่ 2 รายการบนเว็บช่วยให้สร้างตัวควบคุมแบบฟอร์มที่กำหนดเองได้ง่ายขึ้น และลดข้อจำกัดของการควบคุมที่กำหนดเองในปัจจุบัน
- เหตุการณ์
formdata
จะให้ออบเจ็กต์ JavaScript ที่กำหนดเองเข้าร่วมในการส่งแบบฟอร์ม คุณจึงเพิ่มข้อมูลแบบฟอร์มได้โดยไม่ต้องใช้<input>
ที่ซ่อนอยู่ - API องค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มจะทำให้องค์ประกอบที่กำหนดเองทำหน้าที่เหมือนการควบคุมแบบฟอร์มในตัวได้มากขึ้น
ฟีเจอร์ 2 อย่างนี้สามารถใช้เพื่อสร้างการควบคุมประเภทใหม่ๆ ที่ทำงานได้ดียิ่งขึ้น
API ตามเหตุการณ์
เหตุการณ์ formdata
เป็น API ระดับต่ำที่ให้โค้ด JavaScript เข้าร่วมการส่งแบบฟอร์มได้ กลไกมีการทำงานดังนี้
- คุณเพิ่ม Listener เหตุการณ์
formdata
ในแบบฟอร์มที่คุณต้องการโต้ตอบด้วย - เมื่อผู้ใช้คลิกปุ่มส่ง แบบฟอร์มจะเริ่มทํางานเหตุการณ์
formdata
ซึ่งมีออบเจ็กต์FormData
ที่เก็บข้อมูลทั้งหมดที่ส่ง - ผู้ฟัง
formdata
แต่ละคนจะมีโอกาสเพิ่มหรือแก้ไขข้อมูลก่อนที่จะส่งแบบฟอร์ม
ตัวอย่างการส่งค่าเดียวใน Listener เหตุการณ์ formdata
มีดังนี้
const form = document.querySelector('form');
// FormData event is sent on <form> submission, before transmission.
// The event has a formData property
form.addEventListener('formdata', ({formData}) => {
// https://developer.mozilla.org/docs/Web/API/FormData
formData.append('my-input', myInputValue);
});
ลองใช้วิธีนี้โดยใช้ตัวอย่างของเราใน Glitch อย่าลืมเรียกใช้ใน Chrome 77 ขึ้นไปเพื่อดูการทํางานจริงของ API
ความเข้ากันได้กับเบราว์เซอร์
องค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์ม
คุณสามารถใช้ API แบบอิงตามเหตุการณ์กับคอมโพเนนต์ประเภทใดก็ได้ แต่จะอนุญาตให้คุณโต้ตอบกับกระบวนการส่งเท่านั้น
การควบคุมแบบฟอร์มตามมาตรฐานมีส่วนร่วมในหลายส่วนของวงจรแบบฟอร์ม นอกจากการส่ง องค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มมีเป้าหมายเพื่อช่วยลดช่องว่างระหว่างวิดเจ็ตที่กำหนดเองและการควบคุมในตัว องค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มจะตรงกับฟีเจอร์หลายอย่างขององค์ประกอบแบบฟอร์มมาตรฐาน ดังนี้
- เมื่อคุณวางองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มภายใน
<form>
ระบบจะเชื่อมโยงองค์ประกอบนั้นกับแบบฟอร์มโดยอัตโนมัติ เช่นเดียวกับตัวควบคุมที่เบราว์เซอร์มีให้ - ติดป้ายกำกับองค์ประกอบโดยใช้องค์ประกอบ
<label>
ได้ - องค์ประกอบสามารถตั้งค่าที่ส่งโดยอัตโนมัติด้วยแบบฟอร์ม
- องค์ประกอบจะตั้งค่าสถานะเพื่อระบุว่ามีอินพุตที่ถูกต้องหรือไม่ หากหนึ่งในการควบคุมแบบฟอร์มมีข้อมูลที่ไม่ถูกต้อง คุณจะส่งแบบฟอร์มไม่ได้
- องค์ประกอบอาจมี Callback สําหรับส่วนต่างๆ ของวงจรการใช้งานแบบฟอร์ม เช่น เมื่อปิดใช้แบบฟอร์มหรือรีเซ็ตเป็นสถานะเริ่มต้น
- องค์ประกอบรองรับ Pseudoclass มาตรฐาน CSS มาตรฐานสำหรับการควบคุมแบบฟอร์ม เช่น
:disabled
และ:invalid
มีฟีเจอร์เยอะแยะเลย บทความนี้ไม่ได้กล่าวถึงข้อมูลทั้งหมด แต่จะอธิบายข้อมูลพื้นฐานที่จำเป็นในการผสานรวมองค์ประกอบที่กำหนดเองกับฟอร์ม
การกำหนดองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์ม
หากต้องการเปลี่ยนองค์ประกอบที่กำหนดเองเป็นองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์ม คุณต้องทำตามขั้นตอนเพิ่มเติมอีกเล็กน้อย ดังนี้
- เพิ่มพร็อพเพอร์ตี้
formAssociated
แบบคงที่ลงในคลาสองค์ประกอบที่กำหนดเอง ซึ่งจะบอกเบราว์เซอร์ให้ปฏิบัติต่อองค์ประกอบเหมือนการควบคุมแบบฟอร์ม - เรียกใช้เมธอด
attachInternals()
ในองค์ประกอบเพื่อเข้าถึงเมธอดและพร็อพเพอร์ตี้เพิ่มเติมสำหรับการควบคุมแบบฟอร์ม เช่นsetFormValue()
และsetValidity()
- เพิ่มพร็อพเพอร์ตี้และเมธอดทั่วไปที่ตัวควบคุมแบบฟอร์มรองรับ เช่น
name
,value
และvalidity
รายการดังกล่าวจะแสดงในคำจำกัดความขององค์ประกอบพื้นฐานที่กำหนดเองดังนี้
// Form-associated custom elements must be autonomous custom elements--
// meaning they must extend HTMLElement, not one of its subclasses.
class MyCounter extends HTMLElement {
// Identify the element as a form-associated custom element
static formAssociated = true;
constructor() {
super();
// Get access to the internal form control APIs
this.internals_ = this.attachInternals();
// internal value for this control
this.value_ = 0;
}
// Form controls usually expose a "value" property
get value() { return this.value_; }
set value(v) { this.value_ = v; }
// The following properties and methods aren't strictly required,
// but browser-level form controls provide them. Providing them helps
// ensure consistency with browser-provided controls.
get form() { return this.internals_.form; }
get name() { return this.getAttribute('name'); }
get type() { return this.localName; }
get validity() {return this.internals_.validity; }
get validationMessage() {return this.internals_.validationMessage; }
get willValidate() {return this.internals_.willValidate; }
checkValidity() { return this.internals_.checkValidity(); }
reportValidity() {return this.internals_.reportValidity(); }
…
}
customElements.define('my-counter', MyCounter);
เมื่อลงทะเบียนแล้ว คุณจะใช้องค์ประกอบนี้ในทุกที่ที่คุณจะใช้การควบคุมแบบฟอร์มที่เบราว์เซอร์มีให้ ดังนี้
<form>
<label>Number of bunnies: <my-counter></my-counter></label>
<button type="submit">Submit</button>
</form>
การตั้งค่า
เมธอด attachInternals()
จะแสดงออบเจ็กต์ ElementInternals
ที่ให้สิทธิ์เข้าถึง API ตัวควบคุมแบบฟอร์ม ขั้นพื้นฐานที่สุดคือเมธอด setFormValue()
ซึ่งจะกำหนดค่าปัจจุบันของตัวควบคุม
เมธอด setFormValue()
สามารถมีค่าได้ 3 ประเภทดังนี้
- ค่าสตริง
- ออบเจ็กต์
File
- ออบเจ็กต์
FormData
คุณใช้ออบเจ็กต์FormData
เพื่อส่งผ่านค่าหลายค่าได้ (เช่น การควบคุมข้อมูลบัตรเครดิตอาจส่งหมายเลขบัตร วันที่หมดอายุ และรหัสยืนยัน)
วิธีตั้งค่าง่ายๆ
this.internals_.setFormValue(this.value_);
หากต้องการตั้งค่าหลายค่า คุณสามารถทำได้ดังนี้
// Use the control's name as the base name for submitted data
const n = this.getAttribute('name');
const entries = new FormData();
entries.append(n + '-first-name', this.firstName_);
entries.append(n + '-last-name', this.lastName_);
this.internals_.setFormValue(entries);
การตรวจสอบข้อมูลที่ป้อน
ตัวควบคุมของคุณยังสามารถเข้าร่วมการตรวจสอบแบบฟอร์มโดยการเรียกใช้ setValidity()
ในออบเจ็กต์ภายใน
// Assume this is called whenever the internal value is updated
onUpdateValue() {
if (!this.matches(':disabled') && this.hasAttribute('required') &&
this.value_ < 0) {
this.internals_.setValidity({customError: true}, 'Value cannot be negative.');
}
else {
this.internals_.setValidity({});
}
this.internals.setFormValue(this.value_);
}
คุณสามารถจัดรูปแบบองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มด้วย :valid
และ :invalid
Pseudoclasses เช่นเดียวกับตัวควบคุมแบบฟอร์มในตัว
Callback ของวงจร
API องค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มจะมีชุด Callback วงจรเพิ่มเติมเพื่อผูกกับวงจรของแบบฟอร์ม Callback เป็นตัวเลือกที่ไม่บังคับ: ใช้ Callback เฉพาะในกรณีที่องค์ประกอบต้องดําเนินการบางอย่างในวงจร
void formAssociatedCallback(form)
มีการเรียกใช้เมื่อเบราว์เซอร์เชื่อมโยงองค์ประกอบกับองค์ประกอบของแบบฟอร์ม หรือยกเลิกการเชื่อมโยงองค์ประกอบจากองค์ประกอบแบบฟอร์ม
void formDisabledCallback(disabled)
เรียกใช้หลังจากสถานะ disabled
ขององค์ประกอบมีการเปลี่ยนแปลง เนื่องจากแอตทริบิวต์ disabled
ขององค์ประกอบนี้มีการเพิ่มหรือนำออก หรือเนื่องจากสถานะ disabled
มีการเปลี่ยนแปลงใน <fieldset>
ที่เป็นระดับบนขององค์ประกอบนี้ พารามิเตอร์ disabled
แสดงสถานะปิดใช้ใหม่ขององค์ประกอบ ตัวอย่างเช่น องค์ประกอบอาจปิดใช้องค์ประกอบใน Shadow DOM เมื่อปิดใช้
void formResetCallback()
เรียกใช้หลังจากรีเซ็ตแบบฟอร์ม องค์ประกอบควรรีเซ็ตตัวเองเป็นสถานะเริ่มต้นบางประเภท สำหรับองค์ประกอบ <input>
โดยปกติองค์ประกอบนี้จะต้องตั้งค่าพร็อพเพอร์ตี้ value
ให้ตรงกับแอตทริบิวต์ value
ที่ตั้งค่าไว้ในมาร์กอัป (หรือในกรณีของช่องทำเครื่องหมาย ให้ตั้งค่าพร็อพเพอร์ตี้ checked
ให้ตรงกับแอตทริบิวต์ checked
void formStateRestoreCallback(state, mode)
เรียกใช้ใน 1 ใน 2 กรณีต่อไปนี้
- เมื่อเบราว์เซอร์คืนค่าสถานะองค์ประกอบ (เช่น หลังจากการนำทาง หรือเมื่อรีสตาร์ทเบราว์เซอร์) ในกรณีนี้ อาร์กิวเมนต์
mode
คือ"restore"
- เมื่อฟีเจอร์ความช่วยเหลือการป้อนข้อมูลของเบราว์เซอร์ เช่น การป้อนแบบฟอร์มอัตโนมัติ กําหนดค่าไว้ ในกรณีนี้ อาร์กิวเมนต์
mode
คือ"autocomplete"
ประเภทของอาร์กิวเมนต์แรกจะขึ้นอยู่กับวิธีเรียกเมธอด setFormValue()
โปรดดูรายละเอียดเพิ่มเติมที่การคืนค่าสถานะแบบฟอร์ม
กำลังคืนค่าสถานะแบบฟอร์ม
ในบางกรณี เช่น เมื่อกลับไปที่หน้าเว็บหรือรีสตาร์ทเบราว์เซอร์ เบราว์เซอร์อาจพยายามคืนค่าแบบฟอร์มกลับไปเป็นสถานะที่ผู้ใช้ป้อนไว้
สำหรับองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์ม สถานะที่คืนค่ามาจากค่าที่คุณส่งไปยังเมธอด setFormValue()
คุณสามารถเรียกใช้เมธอดด้วยพารามิเตอร์ค่าเดียวดังที่แสดงในตัวอย่างก่อนหน้านี้ หรือเรียกใช้ด้วยพารามิเตอร์ 2 รายการก็ได้ ดังนี้
this.internals_.setFormValue(value, state);
value
แสดงค่าที่ส่งได้ของตัวควบคุม พารามิเตอร์ state
ที่ไม่บังคับคือการแสดงสถานะภายใน ซึ่งอาจมีข้อมูลที่ไม่ได้ส่งไปยังเซิร์ฟเวอร์ พารามิเตอร์ state
ใช้ประเภทเดียวกับพารามิเตอร์ value
ซึ่งอาจเป็นสตริง File
หรือออบเจ็กต์ FormData
พารามิเตอร์ state
มีประโยชน์เมื่อคุณคืนค่าสถานะของการควบคุมตามค่าเพียงอย่างเดียวไม่ได้ ตัวอย่างเช่น สมมติว่าคุณสร้างตัวเลือกสีที่มีหลายโหมด ได้แก่ จานสีหรือวงล้อสี RGB ค่าที่ส่งได้คือสีที่เลือกในรูปแบบ Canonical เช่น "#7fff00"
แต่หากต้องการคืนค่าการควบคุมเป็นสถานะที่เจาะจง คุณยังต้องทราบด้วยว่าอยู่ในโหมดใด ดังนั้น สถานะ อาจมีลักษณะดังนี้ "palette/#7fff00"
this.internals_.setFormValue(this.value_,
this.mode_ + '/' + this.value_);
โค้ดจะต้องคืนค่าสถานะตามค่าสถานะที่จัดเก็บไว้
formStateRestoreCallback(state, mode) {
if (mode == 'restore') {
// expects a state parameter in the form 'controlMode/value'
[controlMode, value] = state.split('/');
this.mode_ = controlMode;
this.value_ = value;
}
// Chrome currently doesn't handle autofill for form-associated
// custom elements. In the autofill case, you might need to handle
// a raw value.
}
ในกรณีของการควบคุมที่ง่ายกว่า (เช่น การป้อนตัวเลข) ค่าดังกล่าวน่าจะเพียงพอที่จะทำให้การควบคุมกลับไปเป็นสถานะก่อนหน้า หากคุณไม่ใส่ state
เมื่อเรียกใช้ setFormValue()
ระบบจะส่งค่าไปยัง formStateRestoreCallback()
formStateRestoreCallback(state, mode) {
// Simple case, restore the saved value
this.value_ = state;
}
ตัวอย่างที่ใช้ได้จริง
ตัวอย่างต่อไปนี้จะรวมฟีเจอร์หลายอย่างขององค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์ม อย่าลืมเรียกใช้ใน Chrome 77 ขึ้นไปเพื่อดูการทํางานจริงของ API
การตรวจหาฟีเจอร์
คุณสามารถใช้การตรวจหาฟีเจอร์เพื่อดูว่าเหตุการณ์ formdata
และองค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์มพร้อมใช้งานหรือไม่ ตอนนี้ยังไม่ได้เผยแพร่ Polyfill สําหรับทั้ง 2 ฟีเจอร์ ในทั้ง 2 กรณี คุณสามารถกลับไปใช้การเพิ่มองค์ประกอบแบบฟอร์มที่ซ่อนไว้เพื่อเผยแพร่ค่าของการควบคุมไปยังแบบฟอร์ม ฟีเจอร์ขั้นสูงขึ้นหลายรายการขององค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์มมีแนวโน้มที่จะทำ Polyfill ได้ยากหรือเป็นไปไม่ได้
if ('FormDataEvent' in window) {
// formdata event is supported
}
if ('ElementInternals' in window &&
'setFormValue' in window.ElementInternals.prototype) {
// Form-associated custom elements are supported
}
บทสรุป
เหตุการณ์ formdata
และองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มมีเครื่องมือใหม่ในการสร้างตัวควบคุมแบบฟอร์มที่กำหนดเอง
เหตุการณ์ formdata
ไม่ได้ให้ความสามารถใหม่ๆ แก่คุณ แต่จะให้อินเทอร์เฟซสำหรับเพิ่มข้อมูลแบบฟอร์มลงในขั้นตอนการส่งโดยไม่ต้องสร้างองค์ประกอบ <input>
ที่ซ่อนอยู่
API องค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์มมีความสามารถชุดใหม่สำหรับการสร้างตัวควบคุมแบบฟอร์มที่กำหนดเองซึ่งทำงานเหมือนการควบคุมแบบฟอร์มในตัว
รูปภาพหลักโดย Oudom Pravat ใน Unsplash