ข้ามไปยังเนื้อหา

Customized Built-ins

สเปกของ Web Components นิยาม custom element ไว้สองแบบ:

แบบextend จากลงทะเบียนเป็นใช้ใน HTML
AutonomousHTMLElementcustomElements.define('x-btn', XBtn)<x-btn>
Customized built-inเช่น HTMLButtonElementcustomElements.define('x-btn', XBtn, { extends: 'button' })<button is="x-btn">

customized built-in extend จาก class ของ HTML element ตัวใดตัวหนึ่งโดยเฉพาะ และประกาศว่า extend element ที่มีมาในตัวตัวใดในอาร์กิวเมนต์ตัวที่สามของ define:

class FancyButton extends HTMLButtonElement {
connectedCallback() {
this.style.background = '#4f46e5';
this.style.color = '#fff';
this.style.borderRadius = '6px';
this.style.padding = '.4rem 1rem';
this.style.border = 'none';
this.style.cursor = 'pointer';
}
}
customElements.define('fancy-button', FancyButton, { extends: 'button' });

ใน HTML attribute is จะเปิดใช้ customized built-in บน element มาตรฐาน:

<button is="fancy-button">Click me</button>

element นี้ยังคงเป็น <button> อยู่ — จึงสืบทอด semantics ที่มีมาในตัวทั้งหมด (โฟกัสได้ ส่ง form ได้ ตอบสนองต่อ disabled) บวกกับพฤติกรรมที่คุณกำหนดเอง

Safari ไม่รองรับ customized built-ins และได้ปฏิเสธอย่างชัดเจนว่าจะไม่รองรับ นี่เป็นปัญหาด้านการพกพาที่สำคัญ: <button is="fancy-button"> จะถูกเรนเดอร์เป็นปุ่มธรรมดาที่ไม่มีสไตล์ใน Safari

มี polyfill อยู่ (เช่น @ungap/custom-elements) แต่เพิ่มน้ำหนักและความซับซ้อน

<!-- This renders correctly in Chrome/Firefox but NOT in Safari: -->
<button is="fancy-button">Safari renders this as a plain button</button>

autonomous custom elements (extends HTMLElement) ได้รับการรองรับอย่างเต็มที่ในทุกที่ สำหรับตัวอย่างปุ่ม นี่คือ autonomous element ที่เทียบเท่ากัน:

class XButton extends HTMLElement {
connectedCallback() {
this.innerHTML =
'<button style="background:#4f46e5;color:#fff;border-radius:6px;' +
'padding:.4rem 1rem;border:none;cursor:pointer">' +
(this.textContent || 'Click me') + '</button>';
}
}
customElements.define('x-button', XButton);

คุณจะเสียการสืบทอด semantics ของ <button> เนทีฟแบบอัตโนมัติไป (คุณต้องเพิ่ม role="button" และจัดการคีย์บอร์ดด้วยตัวเอง) แต่คุณจะได้การรองรับข้าม browser อย่างเต็มที่

เดโมด้านล่างใช้ autonomous element — ทำงานได้ในทุก browser ส่วนไวยากรณ์ของ customized built-in แสดงไว้เพื่อเป็นข้อมูลอ้างอิงเท่านั้น

หมายเหตุเรื่อง browser: รูปแบบ customized built-in (class extends HTMLButtonElement + <button is="...">) ไม่รองรับใน Safari ให้ใช้ autonomous elements (class extends HTMLElement) เพื่อให้ได้ Web Components ที่พกพาได้และทำงานข้าม browser

ตัวเลือกBenefitCost
Customized built-in (is="...")สืบทอด semantics เนทีฟของ element เดิมได้ครบ (focus, form submission, accessibility) โดยไม่ต้องเขียนเองSafari ปฏิเสธที่จะรองรับอย่างถาวร ทำให้พกพาข้าม browser ไม่ได้
Autonomous element (extends HTMLElement)รองรับเต็มรูปแบบในทุก browser หลักต้องเขียน semantics ที่เนทีฟให้ฟรี (เช่น role, keyboard handling) ด้วยตัวเอง
  • ลืมใส่ { extends: 'button' } — ถ้าไม่ส่ง option ตัวที่สามให้ customElements.define browser จะไม่รู้ว่านี่คือ customized built-in และ is="fancy-button" จะไม่ทำงาน
  • ไม่ทดสอบใน Safari ก่อน deploy — ความล้มเหลวเป็นแบบเงียบ ไม่มี error ใน console แค่เรนเดอร์เป็น <button> ธรรมดาไม่มีสไตล์ ทำให้พลาดง่ายถ้าทดสอบแค่ Chrome/Firefox
  • ใส่ polyfill โดยไม่ชั่งน้ำหนักต้นทุน@ungap/custom-elements ช่วยแก้ปัญหาได้ แต่เพิ่ม bundle size และความซับซ้อนให้ทุก request แม้ใน browser ที่รองรับอยู่แล้ว

💡 ตัวอย่างจากของจริง

GitHub — เลือกสร้าง <relative-time> และ <clipboard-copy> เป็น autonomous elements แทนที่จะใช้ customized built-ins เพื่อให้ทำงานได้ในทุก browser รวมถึง Safari

Adobe Spectrum — ดีไซน์ระบบ Spectrum Web Components เลือกใช้ autonomous elements เป็นหลักด้วยเหตุผลด้านการพกพาข้าม browser เดียวกันนี้

คุณเปิดใช้ customized built-in element ใน HTML ได้อย่างไร?
อาร์กิวเมนต์ตัวที่สามของ customElements.define ตัวใดที่บ่งบอกว่าเป็น customized built-in?
browser ตัวใดที่ไม่รองรับ customized built-in elements?