Form-Associated Element
form-associated custom element คืออะไร
หัวข้อที่มีชื่อว่า “form-associated custom element คืออะไร”Form-Associated Custom Elements API ช่วยให้ custom element เข้าร่วมใน <form> ได้เหมือนกับ input แบบเนทีฟทุกประการ element สามารถรายงานค่า (value) และความถูกต้อง (validity) ของตัวเอง รวมถึงถูกรวมเข้าไปใน FormData และ form.submit() ได้
หากไม่มี API นี้ custom element ที่วางอยู่ภายใน <form> จะมองไม่เห็นโดยกลไกการ submit form ของ browser ค่าที่เก็บไว้จะไม่ถูก serialise และการตรวจสอบความถูกต้องในตัว (built-in validation) ก็จะไม่ทำงานกับ element นั้น แต่เมื่อใช้ API นี้ element ของคุณจะกลายเป็นผู้ร่วมใน form ระดับเฟิร์สคลาสที่แยกไม่ออกจาก <input> แบบเนทีฟ
การเปิดใช้งาน API
หัวข้อที่มีชื่อว่า “การเปิดใช้งาน API”มีสองสิ่งที่จำเป็น: property แบบ static formAssociated = true บน class และการเรียก this.attachInternals() ใน constructor
class MyInput extends HTMLElement { static formAssociated = true; constructor() { super(); this.internals = this.attachInternals(); }}ต้องประกาศ property แบบ static นี้ก่อนที่จะเรียก attachInternals() เพราะ browser จะอ่านค่านี้ตอนที่สร้าง element (construction time) เพื่อตัดสินใจว่าจะมอบความสามารถในการเข้าร่วม form ให้หรือไม่
method ของ ElementInternals
หัวข้อที่มีชื่อว่า “method ของ ElementInternals”เมื่อคุณมี object internals แล้ว นี่คือ method ที่เกี่ยวข้องกับ form ที่สำคัญ:
internals.setFormValue(value)— กำหนดค่าที่จะถูกรวมเข้าไปในFormDataเมื่อ form ถูก submitinternals.setValidity(flags, message, anchor)— ควบคุมสถานะการตรวจสอบความถูกต้องของ element ส่ง object ว่างและ string ว่างเพื่อล้าง error ทั้งหมดinternals.reportValidity()— เรียก UI การตรวจสอบความถูกต้องในตัวของ browser (กล่อง tooltip) ในแบบเดียวกับที่ input แบบเนทีฟทำinternals.checkValidity()— คืนค่า boolean ที่บอกว่าตอนนี้ element ผ่านการตรวจสอบความถูกต้องหรือไม่
property internals.role และ property ที่สะท้อน (reflection) ค่า ARIA จะกล่าวถึงในบทเรียน Accessibility
เอกสารอ้างอิง API ฉบับเต็ม
หัวข้อที่มีชื่อว่า “เอกสารอ้างอิง API ฉบับเต็ม”// Form-Associated Custom Elements API (Chrome 77+, Firefox 98+, Safari 16.4+)// static formAssociated = true — must be on the class before attachInternals()// this.internals = this.attachInternals()//// internals.setFormValue(value) — submittable value// internals.setFormValue(value, state) — value + restore-state hint// internals.setValidity({}, '') — clear validity errors// internals.setValidity({ valueMissing: true }, 'Required', anchorEl)// internals.reportValidity() — show browser validation UI// internals.checkValidity() — boolean// internals.form — the associated <form> element// internals.labels — NodeList of associated <label>s// internals.willValidate — booleanเดโมที่รันได้ — ตรวจจับฟีเจอร์ (feature-detected)
หัวข้อที่มีชื่อว่า “เดโมที่รันได้ — ตรวจจับฟีเจอร์ (feature-detected)”เดโมด้านล่างตรวจจับฟีเจอร์ (feature-detect) ว่ารองรับ attachInternals และ formAssociated หรือไม่ หาก browser ไม่รองรับ จะแสดงข้อความสำรอง (fallback) แทน
พิมพ์ลงใน custom input แล้วดูย่อหน้าด้านล่าง form สะท้อนค่าที่จะถูก submit ไปพร้อมกับ form
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
Native form-associated (static formAssociated = true + ElementInternals) | custom element เข้าร่วมกับ <form> ได้เหมือน native control เช่น validate, reset, submit ค่าไปพร้อม form โดยไม่ต้องเขียน JavaScript เพิ่ม | รองรับเฉพาะ browser ที่ค่อนข้างใหม่ และ API ของ ElementInternals ยังมีรายละเอียดปลีกย่อยที่ต้องเรียนรู้ |
Polyfill / native <input> ที่ซ่อนอยู่ข้างใน shadow DOM | รองรับ browser เก่าได้กว้างกว่า | เพิ่มความซับซ้อนของ DOM ต้อง sync ค่าระหว่าง custom element กับ input ที่ซ่อนไว้เอง และเสี่ยงเรื่อง accessibility ถ้า sync พลาด |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ลืมตั้ง
static formAssociated = true— ถ้าไม่ตั้ง flag นี้attachInternals()จะไม่คืนค่าElementInternalsที่เข้าร่วม form ได้ และsetFormValue()จะไม่มีผลใด ๆ ทำให้ค่าของ custom input ไม่เคยถูก submit ไปพร้อม form เลย - เรียก
attachInternals()ในconnectedCallbackแทนconstructor— object internals ต้องถูกสร้างเพียงครั้งเดียวต่อ element ถ้าเรียกซ้ำในconnectedCallback(ซึ่งอาจทำงานหลายครั้ง) จะได้ error เพราะเรียกattachInternalsซ้ำไม่ได้ - ไม่เรียก
setFormValue()ทุกครั้งที่ค่าเปลี่ยน — ถ้าอัปเดต internal state ของ element แต่ลืม sync ไปยัง form value ผ่านinternals.setFormValue()ฟอร์มจะ submit ค่าเก่าหรือค่าว่างแทนค่าที่ผู้ใช้เพิ่งกรอก
💡 ตัวอย่างจากของจริง
Adobe Photoshop on the web — ใช้ Lit ร่วมกับ form-associated custom elements เพื่อให้ custom input controls ของแอปเข้าร่วมกับ native
<form>semantics ได้เต็มรูปแบบShoelace — form control อย่าง
<sl-input>และ<sl-checkbox>implement form-associated custom elements เพื่อให้ทำงานร่วมกับ native<form>submission และ validation ได้เหมือน native element