การเรนเดอร์เนื้อหา
ทำไมต้อง connectedCallback ไม่ใช่ constructor
หัวข้อที่มีชื่อว่า “ทำไมต้อง connectedCallback ไม่ใช่ constructor”class ของ custom element มี constructor เหมือน ES class อื่น ๆ แต่ constructor เป็นที่ที่ผิดสำหรับการทำงานกับ DOM เมื่อ browser เรียก constructor element จะมีอยู่ในรูปอ็อบเจกต์ JavaScript แต่ยังไม่ถูกเพิ่มเข้าไปในเอกสาร — ยังไม่มี parent, attribute อาจยัง parse ไม่ครบ และ children ก็ยังไม่ปรากฏ
ให้ใช้ connectedCallback แทน จะถูกเรียกทุกครั้งที่ element ถูก แทรกเข้าไปในเอกสารที่เชื่อมต่อแล้ว ซึ่ง ณ จุดนั้น attribute และ light DOM children ของ element พร้อมใช้งานแล้ว:
class MyCard extends HTMLElement { constructor() { super(); // always required // DO NOT touch this.innerHTML, this.children, or attributes here }
connectedCallback() { // Safe to read attributes and manipulate DOM here const title = this.getAttribute('title') || 'Card'; this.innerHTML = '<h2>' + title + '</h2>'; }}การกำหนดเนื้อหา: innerHTML เทียบกับ DOM API
หัวข้อที่มีชื่อว่า “การกำหนดเนื้อหา: innerHTML เทียบกับ DOM API”innerHTML คือวิธีที่เร็วที่สุดในการสร้างเทมเพลตออกมา:
connectedCallback() { this.innerHTML = '<p>Hello from <strong>innerHTML</strong></p>';}สำหรับค่าที่เปลี่ยนแปลงได้หรือค่าที่ผู้ใช้ป้อนเข้ามา ให้ sanitize ก่อนเสมอ หรือใช้ DOM API แทนเพื่อหลีกเลี่ยง XSS:
connectedCallback() { const p = document.createElement('p'); const userInput = this.getAttribute('label') || ''; p.textContent = userInput; // textContent never parses HTML this.appendChild(p);}การอ่าน attribute
หัวข้อที่มีชื่อว่า “การอ่าน attribute”this.getAttribute(name) คืนค่า attribute เป็นสตริง หรือคืน null หาก attribute ไม่มีอยู่ รูปแบบ this.getAttribute('x') || 'default' ปลอดภัยเพราะ null || 'default' ให้ผลเป็น 'default':
connectedCallback() { const color = this.getAttribute('color') || 'blue'; const label = this.getAttribute('label') || 'Button'; this.innerHTML = '<button style="background:' + color + '">' + label + '</button>';}เดโมที่รันได้
หัวข้อที่มีชื่อว่า “เดโมที่รันได้”ลองเพิ่ม tag <color-card> ตัวที่สองโดยใช้ color และ label ที่ต่างกันในแผง HTML
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
innerHTML | เขียนเทมเพลตได้เร็วและกระชับ | เสี่ยง XSS ถ้าใส่ค่าที่ผู้ใช้ป้อนเข้ามาโดยไม่ sanitize |
DOM API (createElement, textContent) | ปลอดภัยจาก XSS เพราะ textContent ไม่ parse HTML | เขียนโค้ดยาวและอ่านยากกว่าเมื่อโครงสร้างซับซ้อน |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- จัดการ DOM ใน constructor แทน
connectedCallback— ตอนนั้น element ยังไม่ถูกแทรกเข้าเอกสาร attribute อาจยัง parse ไม่ครบ ทำให้getAttributeคืนค่าไม่ตรงที่คาด - ใส่ค่าจากผู้ใช้ลงใน
innerHTMLโดยตรง — เช่นthis.innerHTML = userInputเปิดช่องให้เกิด XSS ควรใช้textContentหรือ sanitize ก่อนเสมอ - ลืมว่า
getAttributeคืนnullไม่ใช่สตริงว่าง — ถ้าไม่ใส่ fallback ด้วย|| 'default'ค่าที่ต่อ string จะกลายเป็นคำว่า"null"ปนอยู่ในผลลัพธ์
💡 ตัวอย่างจากของจริง
GitHub —
<clipboard-copy>ใช้ DOM API และtextContentในการอ่าน/เขียนเนื้อหาแทนinnerHTMLแบบดิบ เพื่อป้องกันความเสี่ยงด้านความปลอดภัยใน productionAdobe Spectrum — component ใน Spectrum Web Components เรนเดอร์เนื้อหาใน
connectedCallbackตามแพตเทิร์นมาตรฐานของสเปก เพื่อให้ attribute พร้อมใช้งานก่อนแตะ DOM เสมอ