Attributes, Properties & Events
Custom element ทุกตัวมีสองช่องทางสำหรับรับข้อมูลและหนึ่งช่องทางสำหรับส่งข้อมูลออก Attributes เข้ามาในรูปแบบ string ผ่าน HTML markup Properties เข้ามาในรูปแบบ JavaScript value ที่กำหนดโดยโค้ด ส่วน Events พาข้อมูลออกสู่ส่วนอื่นของ application การเข้าใจว่าควรใช้ช่องทางไหน — และเมื่อใด — คือกุญแจสำคัญในการเขียน custom elements ที่ทำงานได้เป็นธรรมชาติและผสานกับ framework ใดก็ได้หรือ JavaScript ล้วน
โมดูลนี้ครอบคลุมห้าบทเรียน:
index— ภาพรวม attributes vs properties vs events (หน้านี้)reflecting-attributes— การซิงก์ attributes และ properties ให้ตรงกันproperties-vs-attributes— เมื่อไหร่ควรใช้อะไร และข้อควรระวังเรื่อง type-coercioncustom-events— การ dispatch และ listenCustomEventcomposed-events— การข้าม shadow-DOM boundary ด้วยcomposed: true
กระแสข้อมูลเข้าและออกจาก Custom Element
หัวข้อที่มีชื่อว่า “กระแสข้อมูลเข้าและออกจาก Custom Element”flowchart LR HTML["HTML Markup"] -->|attributes| CE["Custom Element"] JS["JavaScript"] -->|properties| CE CE -->|events| App["App / Parent"]
| ช่องทาง | ทิศทาง | ประเภทข้อมูล | กำหนดจาก |
|---|---|---|---|
| Attribute | เข้า | string | HTML หรือ setAttribute() |
| Property | เข้า | JS value ใดก็ได้ | การ assign ใน JavaScript |
| Event | ออก | Event / CustomEvent | dispatchEvent() ภายใน element |
Attributes คือ interface ตามธรรมชาติจาก HTML — เป็นสิ่งที่เราเขียนใน markup และ HTML parser เข้าใจได้ Properties คือ JavaScript interface — รองรับ type ใดก็ได้: ตัวเลข, boolean, array, object ส่วน Events คือวิธีเดียวที่ถูกต้องสำหรับ element ในการสื่อสารออกไปภายนอก ซึ่งทำให้ element แยกออกจากผู้ใช้งานอย่างสมบูรณ์
Attributes เป็น string เสมอ
หัวข้อที่มีชื่อว่า “Attributes เป็น string เสมอ”เมื่อ browser แปลง <attr-demo label="42"> ค่า "42" คือ string หาก element ต้องการตัวเลข โค้ดของคุณต้องแปลงค่าเอง นี่คือสาเหตุที่พบบ่อยที่สุดของบัก subtle เมื่อย้ายจาก framework component (ที่ props มี type ใดก็ได้) มาสู่ custom elements
class AttrDemo extends HTMLElement { static get observedAttributes() { return ['count']; }
attributeChangedCallback(name, oldVal, newVal) { // newVal is always a string — coerce explicitly var count = Number(newVal); this.textContent = 'Count: ' + count; }}customElements.define('attr-demo', AttrDemo);Properties ข้ามข้อจำกัดเรื่อง string
หัวข้อที่มีชื่อว่า “Properties ข้ามข้อจำกัดเรื่อง string”การกำหนด property โดยตรงใน JavaScript ช่วยให้ส่งค่าที่ซับซ้อนได้โดยไม่ต้องแปลงเป็น string:
var el = document.querySelector('my-chart');el.data = [{ x: 1, y: 2 }, { x: 3, y: 4 }]; // object, not a stringsetter ของ element จะรับค่าตามที่ส่งมา ไม่มีการแปลงเป็น string เว้นแต่คุณจะเขียนเองอย่างชัดเจน นี่คือเหตุผลที่การกำหนดค่าที่ซับซ้อน — array, object, function — ควรอยู่บน properties ไม่ใช่ attributes
Events พาข้อมูลออก
หัวข้อที่มีชื่อว่า “Events พาข้อมูลออก”Element ไม่ควรเข้าถึง DOM เพื่อแก้ไข parent โดยตรง แต่ควร dispatch event และปล่อยให้ parent ตัดสินใจว่าจะทำอะไร:
this.dispatchEvent(new CustomEvent('demo-click', { bubbles: true, composed: true, detail: { label: this.getAttribute('label') }}));Parent ฟังด้วย addEventListener('demo-click', handler) Element และ parent แยกออกจากกันอย่างสมบูรณ์
เดโมใช้งานจริง
หัวข้อที่มีชื่อว่า “เดโมใช้งานจริง”Element ด้านล่างอ่าน attribute label และแสดงผลในกล่องที่มีสไตล์ คลิกที่กล่องเพื่อ dispatch event demo-click — หน้าเพจจะจับ event นั้นและแสดง log entry