เผยแพร่: 8 สิงหาคม 2562
นักพัฒนาจำนวนมากสร้างตัวควบคุมแบบฟอร์มที่กำหนดเอง ไม่ว่าจะเป็นเพื่อให้มีตัวควบคุมที่ไม่มีอยู่ในเบราว์เซอร์ หรือเพื่อปรับแต่งรูปลักษณ์และความรู้สึกให้เกินกว่าที่เป็นไปได้ด้วยตัวควบคุมแบบฟอร์มในตัว
อย่างไรก็ตาม การจำลองคุณลักษณะของตัวควบคุมฟอร์ม HTML ในตัวอาจเป็นเรื่องยาก พิจารณาคุณลักษณะบางอย่างที่องค์ประกอบ <input> ได้รับโดยอัตโนมัติเมื่อคุณเพิ่มลงในแบบฟอร์ม:
- อินพุตจะถูกเพิ่มลงในรายการควบคุมของแบบฟอร์มโดยอัตโนมัติ
- ค่าอินพุตจะถูกส่งโดยอัตโนมัติพร้อมกับแบบฟอร์ม
- อินพุตมีส่วนร่วมในการตรวจสอบแบบฟอร์ม คุณสามารถกำหนดรูปแบบอินพุตโดยใช้ซูโดคลาส
:validและ:invalid - ระบบจะแจ้งเตือนอินพุตเมื่อฟอร์มถูกรีเซ็ต เมื่อฟอร์มถูกโหลดซ้ำ หรือเมื่อเบราว์เซอร์พยายามกรอกรายการฟอร์มโดยอัตโนมัติ
การควบคุมแบบฟอร์มที่กำหนดเองโดยทั่วไปจะมีคุณลักษณะเหล่านี้เพียงไม่กี่อย่าง นักพัฒนาสามารถหลีกเลี่ยงข้อจำกัดบางประการใน JavaScript ได้ เช่น การเพิ่ม <input> ที่ซ่อนอยู่ในแบบฟอร์มเพื่อมีส่วนร่วมในการส่งแบบฟอร์ม แต่คุณลักษณะอื่นๆ ไม่สามารถจำลองได้ใน JavaScript เพียงอย่างเดียว
คุณลักษณะเว็บสองประการทำให้การสร้างตัวควบคุมแบบฟอร์มที่กำหนดเองเป็นเรื่องง่ายขึ้น และลบข้อจำกัดของตัวควบคุมที่กำหนดเอง:
- เหตุการณ์
formdataอนุญาตให้วัตถุ JavaScript ที่ต้องการมีส่วนร่วมในการส่งแบบฟอร์ม ดังนั้นคุณจึงสามารถเพิ่มข้อมูลแบบฟอร์มได้โดยไม่ต้องใช้<input>ที่ซ่อนอยู่ - API องค์ประกอบแบบกำหนดเองที่เกี่ยวข้องกับแบบฟอร์มช่วยให้องค์ประกอบแบบกำหนดเองทำหน้าที่คล้ายกับการควบคุมแบบฟอร์มในตัวได้มากขึ้น
คุณลักษณะทั้งสองนี้สามารถนำมาใช้เพื่อสร้างการควบคุมประเภทใหม่ที่ทำงานได้ดีขึ้น
API ตามเหตุการณ์
เหตุการณ์ formdata เป็น API ระดับต่ำที่ให้โค้ด JavaScript ใดๆ มีส่วนร่วมในการส่งแบบฟอร์ม
- เพิ่มตัวรับฟังเหตุการณ์
formdataลงในแบบฟอร์มที่คุณต้องการโต้ตอบด้วย - เมื่อผู้ใช้คลิกส่ง แบบฟอร์มจะทริกเกอร์เหตุการณ์
formdataซึ่งรวมถึงอ็อบเจ็กต์FormDataที่เก็บข้อมูลทั้งหมดที่ถูกส่งมา - ผู้ฟัง
formdataแต่ละคนจะได้รับโอกาสในการเพิ่มหรือแก้ไขข้อมูลก่อนที่จะส่งแบบฟอร์ม
นี่คือตัวอย่างของการส่งค่าเดียวในตัวรับฟังเหตุการณ์ 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);
});
องค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์ม
คุณใช้ API ที่อิงตามเหตุการณ์กับคอมโพเนนต์ทุกประเภทได้ แต่จะช่วยให้คุณโต้ตอบกับกระบวนการส่งเท่านั้น
การควบคุมแบบฟอร์มมาตรฐานมีส่วนร่วมในหลายส่วนของวงจรชีวิตแบบฟอร์ม องค์ประกอบแบบกำหนดเองที่เกี่ยวข้องกับแบบฟอร์มมีจุดมุ่งหมายเพื่อเชื่อมช่องว่างระหว่างวิดเจ็ตแบบกำหนดเองและการควบคุมในตัว Custom Elements ที่เชื่อมโยงกับแบบฟอร์ม จะตรงกับฟีเจอร์หลายอย่างขององค์ประกอบแบบฟอร์มที่ได้มาตรฐาน ดังนี้
- เมื่อคุณวางองค์ประกอบที่กำหนดเองซึ่งเชื่อมโยงกับแบบฟอร์มภายใน
<form>องค์ประกอบดังกล่าวจะเชื่อมโยงกับแบบฟอร์มโดยอัตโนมัติ เช่นเดียวกับตัวควบคุมที่เบราว์เซอร์จัดเตรียมไว้ - องค์ประกอบสามารถติดป้ายกำกับได้โดยใช้องค์ประกอบ
<label> - องค์ประกอบสามารถตั้งค่าที่ส่งโดยอัตโนมัติพร้อมกับแบบฟอร์มได้
- องค์ประกอบสามารถตั้งค่าแฟล็กเพื่อระบุว่ามีอินพุตที่ถูกต้องหรือไม่ หากการควบคุมแบบฟอร์มหนึ่งมีอินพุตที่ไม่ถูกต้อง จะไม่สามารถส่งแบบฟอร์มนั้นได้
- องค์ประกอบนี้สามารถระบุการเรียกกลับสำหรับส่วนต่างๆ ของวงจรของแบบฟอร์มได้ เช่น เมื่อแบบฟอร์มถูกปิดใช้หรือรีเซ็ตเป็นสถานะเริ่มต้น
- องค์ประกอบนี้รองรับ CSS pseudoclasses มาตรฐานสำหรับการควบคุมแบบฟอร์ม เช่น
:disabledและ:invalid
เอกสารนี้ไม่ได้ครอบคลุมทุกอย่าง แต่จะอธิบายพื้นฐานที่จำเป็นในการ ผสานรวมองค์ประกอบที่กำหนดเองกับแบบฟอร์ม
กำหนดองค์ประกอบที่กำหนดเองที่เกี่ยวข้องกับแบบฟอร์ม
การเปลี่ยนองค์ประกอบที่กำหนดเองให้เป็นองค์ประกอบที่กำหนดเองที่เกี่ยวข้องกับแบบฟอร์มต้องมีขั้นตอนเพิ่มเติมบางประการ:
- เพิ่มคุณสมบัติคงที่
formAssociatedให้กับคลาสองค์ประกอบแบบกำหนดเองของคุณ สิ่งนี้แจ้งให้เบราว์เซอร์ปฏิบัติต่อองค์ประกอบเหมือนกับตัวควบคุมแบบฟอร์ม - เรียกใช้เมธอด
attachInternals()ในองค์ประกอบเพื่อเข้าถึงเมธอดและพร็อพเพอร์ตี้เพิ่มเติมสำหรับตัวควบคุมแบบฟอร์ม เช่นsetFormValue()และsetValidity() - เพิ่มคุณสมบัติทั่วไปและวิธีการที่รองรับโดยตัวควบคุมแบบฟอร์ม เช่น
name,valueและvalidity
นี่คือวิธีที่รายการเหล่านั้นพอดีกับคำจำกัดความองค์ประกอบที่กำหนดเองพื้นฐาน:
// Form-associated custom elements must be autonomous custom elements.
// 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 เช่นเดียวกับตัวควบคุมแบบฟอร์มในตัว
Lifecycle Callback
API องค์ประกอบแบบกำหนดเองที่เกี่ยวข้องกับแบบฟอร์มประกอบด้วยชุดการโทรกลับวงจรชีวิตพิเศษเพื่อเชื่อมโยงกับวงจรชีวิตแบบฟอร์ม การโทรกลับเป็นทางเลือก: ใช้การโทรกลับเฉพาะเมื่อองค์ประกอบของคุณต้องดำเนินการบางอย่างในจุดนั้นของวงจรชีวิต
void formAssociatedCallback(form)
เรียกใช้เมื่อเบราว์เซอร์เชื่อมโยงองค์ประกอบกับองค์ประกอบของแบบฟอร์ม หรือ ยกเลิกการเชื่อมโยงองค์ประกอบจากองค์ประกอบของแบบฟอร์ม
void formDisabledCallback(disabled)
เรียกใช้หลังจากสถานะ disabled ขององค์ประกอบเปลี่ยนแปลง ไม่ว่าจะเป็นเพราะมีการเพิ่มหรือนำแอตทริบิวต์ disabled ขององค์ประกอบนี้ออก หรือเนื่องจากสถานะ disabled เปลี่ยนแปลงใน <fieldset>
ซึ่งเป็นบรรพบุรุษขององค์ประกอบนี้
เช่น องค์ประกอบอาจปิดใช้องค์ประกอบใน Shadow DOM เมื่อปิดใช้
void formResetCallback()
เรียกหลังจากฟอร์มถูกรีเซ็ตแล้ว องค์ประกอบควรรีเซ็ตตัวเองเป็นสถานะเริ่มต้นบางอย่าง
สำหรับองค์ประกอบ <input> โดยปกติแล้วจะเกี่ยวข้องกับการตั้งค่าคุณสมบัติ value ให้ตรงกับแอตทริบิวต์ value ที่ตั้งไว้ในมาร์กอัป เมื่อใช้ช่องทําเครื่องหมาย การดำเนินการนี้จะ
เกี่ยวข้องกับการตั้งค่าพร็อพเพอร์ตี้ checked ให้ตรงกับแอตทริบิวต์ checked
void formStateRestoreCallback(state, mode)
เรียกใช้ใน 2 กรณีต่อไปนี้
- เมื่อเบราว์เซอร์คืนสถานะขององค์ประกอบ เช่น หลังจากการนำทางหรือเมื่อเบราว์เซอร์รีสตาร์ท อาร์กิวเมนต์
modeคือ"restore" - เมื่อฟีเจอร์ช่วยป้อนข้อมูลของเบราว์เซอร์ เช่น การกรอกแบบฟอร์มอัตโนมัติ ตั้งค่า อาร์กิวเมนต์
modeคือ"autocomplete"
ประเภทของอาร์กิวเมนต์แรกจะขึ้นอยู่กับวิธีเรียกใช้เมธอด setFormValue()
คืนค่าสถานะแบบฟอร์ม
ภายใต้สถานการณ์บางอย่าง เช่น เมื่อนำทางกลับไปยังหน้าหรือรีสตาร์ทเบราว์เซอร์ เบราว์เซอร์อาจพยายามคืนฟอร์มให้กลับเป็นสถานะที่ผู้ใช้ทิ้งไว้
สำหรับองค์ประกอบแบบกำหนดเองที่เกี่ยวข้องกับแบบฟอร์ม สถานะที่คืนค่าจะมาจากค่าที่คุณส่งไปยังวิธี setFormValue() คุณเรียกใช้เมธอดด้วยพารามิเตอร์ค่าเดียวได้ ดังที่แสดงในตัวอย่างก่อนหน้า
หรือด้วยพารามิเตอร์ 2 รายการ
this.internals_.setFormValue(value, state);
value แสดงถึงค่าที่ส่งได้ของการควบคุม พารามิเตอร์ state ที่เป็นทางเลือกเป็นการแสดงสถานะ ภายใน ของการควบคุม ซึ่งอาจรวมถึงข้อมูลที่ไม่ได้ถูกส่งไปยังเซิร์ฟเวอร์ พารามิเตอร์ state ใช้ประเภทเดียวกันกับพารามิเตอร์ value: สตริง File หรืออ็อบเจ็กต์ FormData
พารามิเตอร์ state มีประโยชน์เมื่อคุณไม่สามารถคืนค่าสถานะของการควบคุมโดยอิงจากค่าเพียงอย่างเดียวได้ ตัวอย่างเช่น สมมติว่าคุณสร้างตัวเลือกสีที่มีโหมดหลายโหมด: จานสีหรือวงล้อสี RGB ค่าที่ส่งได้คือสีที่เลือกในรูปแบบมาตรฐาน เช่น "#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 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;
}
การตรวจจับคุณลักษณะ
คุณสามารถใช้การตรวจหาฟีเจอร์เพื่อพิจารณาว่าเหตุการณ์ formdata และ
องค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์มพร้อมใช้งานหรือไม่ ไม่มีการเปิดตัว polyfill สำหรับฟีเจอร์ทั้งสองนี้ ในทั้งสองกรณี คุณสามารถย้อนกลับไปที่การเพิ่มองค์ประกอบฟอร์มที่ซ่อนอยู่เพื่อเผยแพร่ค่าของการควบคุมไปยังฟอร์มได้
ฟีเจอร์ขั้นสูงหลายอย่างขององค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์มอาจ ทำ 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 จะมีอินเทอร์เฟซให้คุณเพิ่มข้อมูลแบบฟอร์มลงในกระบวนการส่ง
โดยไม่ต้องสร้างองค์ประกอบ <input> ที่ซ่อนไว้
API ขององค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์มช่วยให้คุณมอบความสามารถชุดใหม่
สำหรับตัวควบคุมแบบฟอร์มที่กำหนดเองซึ่งทำงานเหมือนตัวควบคุมแบบฟอร์มในตัว