Properties vs Attributes
Attributes อยู่ใน HTML และเป็น string เสมอ
หัวข้อที่มีชื่อว่า “Attributes อยู่ใน HTML และเป็น string เสมอ”เมื่อเขียน <my-card count="5" label="hello"> ใน HTML browser จะเก็บค่าเหล่านั้นเป็น attribute nodes — และทุกค่าของ attribute คือ string เสมอ ไม่ว่าจะพิมพ์อะไรก็ตาม การเรียก element.getAttribute('count') จะคืนค่า string "5" ไม่ใช่ตัวเลข 5
const el = document.querySelector('my-card');console.log(el.getAttribute('count')); // "5" (string)console.log(el.getAttribute('label')); // "hello" (string)console.log(el.getAttribute('missing')); // null (not present)สิ่งสำคัญที่ต้องจำคือ getAttribute คืนค่าเป็น string หรือ null เท่านั้น — ไม่เคยเป็น boolean, number, array หรือ object
Properties คือค่า JavaScript
หัวข้อที่มีชื่อว่า “Properties คือค่า JavaScript”Properties คือ property ของ JavaScript object ปกติบน element instance สามารถเป็น JS type ใดก็ได้ ไม่ว่าจะเป็น boolean, number, array, object หรือ function เข้าถึงด้วย dot notation ไม่ใช่ getAttribute
el.count = 5; // numberel.rows = [{ id: 1 }]; // arrayel.config = { theme: 'dark', size: 'lg' }; // objectel.disabled = true; // booleanค่า property เหล่านี้จะไม่ถูก serialize กลับเข้า DOM เว้นแต่ component จะทำเองอย่างชัดเจน property ที่กำหนดบน element instance อยู่ใน JavaScript memory เท่านั้น
Boolean attributes: ตรวจสอบการมีอยู่ ไม่ใช่ค่า
หัวข้อที่มีชื่อว่า “Boolean attributes: ตรวจสอบการมีอยู่ ไม่ใช่ค่า”Boolean attributes ใน HTML ทำงานโดย การมีอยู่หรือไม่มีอยู่ — ถ้า attribute นั้นอยู่บน element แสดงว่าเป็น true ถ้าไม่มีแสดงว่าเป็น false ค่าของ attribute string ไม่มีผลใดๆ
นั่นหมายความว่า <my-btn disabled=""> และ <my-btn disabled="false"> ต่างก็ ถูก disabled เพราะ attribute นั้นมีอยู่ มีเพียง <my-btn> (ไม่มี attribute เลย) เท่านั้นที่หมายถึงไม่ถูก disabled
อย่าเปรียบเทียบ getAttribute('disabled') === 'true' หรือ === true ให้ใช้ hasAttribute หรือตรวจสอบว่าค่าที่คืนมาเป็น null หรือไม่เสมอ
// Boolean attribute — check presence, not valueclass MyButton extends HTMLElement { get disabled() { return this.hasAttribute('disabled'); } set disabled(v) { v ? this.setAttribute('disabled', '') : this.removeAttribute('disabled'); }}สังเกตว่า setter กำหนด attribute เป็น empty string '' เมื่อค่าเป็น truthy — นั่นคือ pattern ที่ถูกต้องตามแบบ HTML getter ไม่สนใจว่า string ที่เก็บอยู่คืออะไร แค่ถามว่า attribute นั้นมีอยู่หรือเปล่า?
ข้อมูลซับซ้อน: ใช้ property ไม่ใช่ attribute
หัวข้อที่มีชื่อว่า “ข้อมูลซับซ้อน: ใช้ property ไม่ใช่ attribute”เมื่อต้องการส่ง array, object หรือค่าที่ไม่ใช่ string เข้าไปใน component JS property setter คือเครื่องมือที่เหมาะสม การพยายาม serialize object เป็น attribute string (เช่น data="[object Object]") นั้นเปราะบางและสูญเสียข้อมูล
// Rich data — use a property, not an attributeclass DataTable extends HTMLElement { set rows(data) { // data is an array of objects this._rows = data; this._render(); } get rows() { return this._rows || []; } _render() { /* ... */ }}จากนั้นใช้งานผ่าน JavaScript:
const table = document.querySelector('data-table');table.rows = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' },];setter รับ array จริงๆ — ไม่ต้อง parse เลย เมื่อข้อมูลเปลี่ยน แค่กำหนด property ใหม่และ _render จะทำงาน
Demo สด
หัวข้อที่มีชื่อว่า “Demo สด”element <user-card> ด้านล่างรวมทั้งสองเทคนิคเข้าด้วยกัน:
- boolean attribute
premium— เมื่อมีอยู่จะแสดง crown - property setter
profile— รับ object{ name, role }และ re-render
HTML กำหนด attribute premium ตรงใน markup JavaScript จากนั้นกำหนด el.profile = { name: 'Alice', role: 'Admin' } — เป็น plain object — ซึ่ง trigger property setter และ re-render card
สรุป: ใช้อะไรดี?
หัวข้อที่มีชื่อว่า “สรุป: ใช้อะไรดี?”| สถานการณ์ | ใช้ |
|---|---|
| ค่า config ที่เป็น string ทั่วไป (label, type, variant) | Attribute |
| flag ชนิด Boolean (disabled, hidden, selected) | Boolean attribute + hasAttribute |
| Number, object, array หรือ function | Property setter |
| ค่าที่กำหนดจาก HTML markup เท่านั้น | Attribute |
| ค่าที่กำหนดจาก JavaScript | ได้ทั้งคู่ — แนะนำ property สำหรับค่าที่ไม่ใช่ string |
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Attribute | มองเห็นได้ตรงใน HTML markup และ DevTools ใช้เป็น CSS attribute selector ได้ รองรับ server-rendered HTML โดยไม่ต้องใช้ JavaScript | เก็บได้แค่ string เสมอ ส่งข้อมูลซับซ้อน เช่น array หรือ object เข้าไปตรงๆ ไม่ได้ |
| Property | รับค่า JS type ใดก็ได้ ไม่ว่าจะเป็น object, array หรือ function เข้าถึงเร็วกว่าผ่าน dot notation | ไม่ปรากฏใน HTML markup หรือแผง Elements ของ DevTools ทำให้ debug จาก markup อย่างเดียวยากขึ้น |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- เทียบ
getAttribute('disabled') === 'true'— boolean attribute ทำงานด้วยการมีอยู่ ไม่ใช่ค่าของ string ต้องใช้hasAttributeแทนเสมอ - ใช้
JSON.stringify()ยัด object เข้า attribute string เช่นdata='{"a":1}'— attribute เก็บได้แค่ string เปราะบางและ parse ผิดพลาดง่าย ควรส่งผ่าน property setter โดยตรงแทน - คาดหวังว่ากำหนด property แล้ว attribute จะอัปเดตตามอัตโนมัติ — ถ้าไม่ได้เขียน reflection pattern เอง (getter/setter ที่เรียก
setAttribute) การกำหนด property เปล่าๆ จะไม่ sync กลับไปที่ attribute ทำให้ state ที่เห็นใน markup กับใน JS ไม่ตรงกัน
💡 ตัวอย่างจากของจริง
Element
<input>ดั้งเดิมของ browser คือตัวอย่างที่ชัดเจนที่สุดของความแตกต่างนี้ — attributevalueคือค่าเริ่มต้น (default value) ที่ตั้งมาจาก HTML ส่วน property.valueคือค่าปัจจุบันที่ผู้ใช้พิมพ์อยู่ตอนนี้ ทั้งสองค่าต่างกันได้ทันทีที่ผู้ใช้เริ่มพิมพ์GitHub’s Catalyst framework แนะนำให้ custom element เปิด property สำหรับข้อมูลซับซ้อนเสมอ และสงวน attribute ไว้สำหรับค่า config ที่เป็น string ธรรมดาเท่านั้น