Now.js Framework Documentation
Modal - ระบบ Modal Dialog
Modal - ระบบ Modal Dialog
เอกสารฉบับนี้อธิบาย Modal class ซึ่งเป็นคอมโพเนนต์สำหรับสร้างและจัดการ modal dialogs (กล่องโต้ตอบแบบป๊อปอัพ) ที่มีฟีเจอร์ครบถ้วน รองรับ animation, accessibility และ data binding
📋 สารบัญ
- ภาพรวม
- การติดตั้งและนำเข้า
- การใช้งานพื้นฐาน
- ตัวเลือกการกำหนดค่า
- Data Binding
- Lifecycle Callbacks
- Accessibility
- JavaScript API
- ตัวอย่างการใช้งาน
- API Reference
- แนวทางปฏิบัติที่ดี
ภาพรวม
Modal เป็น class สำหรับสร้าง modal dialogs ที่มีฟีเจอร์ครบถ้วน รองรับการแสดงผลแบบ overlay พร้อม backdrop, animation, keyboard navigation และ data binding
ฟีเจอร์หลัก
- ✅ สร้าง Modal แบบ Programmatic: สร้าง modal ด้วย JavaScript ได้ทันที
- ✅ Animation Support: รองรับ fade-in/fade-out animations
- ✅ Backdrop Management: จัดการ backdrop อัตโนมัติผ่าน
BackdropManager - ✅ Keyboard Support: ปิด modal ด้วยปุ่ม ESC
- ✅ Focus Management: จัดการ focus และ focus trap อัตโนมัติ
- ✅ Accessibility: รองรับ ARIA attributes และ
inertproperty - ✅ Data Binding: ผูกข้อมูลกับ elements ใน modal ได้
- ✅ Content Sanitization: ป้องกัน XSS ด้วยการ sanitize ข้อมูลอัตโนมัติ
- ✅ Lifecycle Callbacks: รองรับ callbacks สำหรับแต่ละขั้นตอน
- ✅ Element Cleanup: ทำความสะอาด elements อัตโนมัติเมื่อปิด modal
เมื่อไหร่ควรใช้ Modal
✅ ใช้ Modal เมื่อ:
- ต้องการสร้าง modal dialog แบบ programmatic
- ต้องการ modal ที่มี animation และ accessibility ครบถ้วน
- ต้องการผูกข้อมูลกับ modal content
- ต้องการควบคุม modal ผ่าน JavaScript API
❌ ไม่ควรใช้ Modal เมื่อ:
- มี modal elements อยู่ใน HTML แล้ว (ใช้
ModalDataBinderแทน) - ต้องการ modal แบบง่ายๆ ไม่ต้องการ features พิเศษ
- ต้องการ modal ที่ไม่มี backdrop
การติดตั้งและนำเข้า
Modal โหลดมาพร้อมกับ Now.js Framework และพร้อมใช้งานทันทีผ่าน window object:
// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.Modal); // Modal classสิ่งที่ต้องพึ่งพา
- BackdropManager – สำหรับจัดการ backdrop (ทางเลือก)
- FormManager – สำหรับ cleanup forms ใน modal (ทางเลือก)
- ElementManager – สำหรับ cleanup elements ใน modal (ทางเลือก)
การใช้งานพื้นฐาน
1. สร้าง Modal แบบง่าย
// สร้าง modal instance
const modal = new Modal({
title: 'ยินดีต้อนรับ',
content: '<p>นี่คือ modal แรกของคุณ!</p>'
});
// แสดง modal
modal.show();2. สร้าง Modal พร้อม Options
const modal = new Modal({
id: 'my-modal',
title: 'ข้อมูลผู้ใช้',
content: `
<div class="user-info">
<img data-modal-target="avatar" class="avatar">
<h3 data-modal-target="name"></h3>
<p data-modal-target="email"></p>
</div>
`,
width: '500px',
closeButton: true,
backdrop: true,
keyboard: true,
animation: true
});
modal.show();3. ปิด Modal
// ปิด modal
modal.hide();
// หรือกดปุ่ม ESC
// หรือคลิกที่ backdrop (ถ้าเปิดใช้งาน)ตัวเลือกการกำหนดค่า
Modal รองรับ options เหล่านี้:
Options พื้นฐาน
| Option | Type | Default | Description |
|---|---|---|---|
id |
string | auto-generated | ID ของ modal element |
title |
string | '' |
หัวข้อของ modal |
content |
string|Node | '' |
เนื้อหาของ modal (HTML string หรือ DOM node) |
width |
string | 'auto' |
ความกว้างของ modal |
maxWidth |
string | '90%' |
ความกว้างสูงสุด |
height |
string | 'auto' |
ความสูงของ modal |
maxHeight |
string | '90vh' |
ความสูงสูงสุด |
className |
string | '' |
CSS class เพิ่มเติม |
Options การแสดงผล
| Option | Type | Default | Description |
|---|---|---|---|
closeButton |
boolean | true |
แสดงปุ่มปิด |
animation |
boolean | true |
เปิดใช้งาน animation |
backdrop |
boolean | true |
ปิด modal เมื่อคลิก backdrop |
keyboard |
boolean | true |
ปิด modal เมื่อกดปุ่ม ESC |
focus |
boolean | true |
จัดการ focus อัตโนมัติ |
Options Backdrop
| Option | Type | Default | Description |
|---|---|---|---|
backdropOpacity |
number | 0.5 |
ความทึบของ backdrop |
backdropColor |
string | 'rgba(0,0,0,0.5)' |
สีของ backdrop |
Lifecycle Callbacks
| Option | Type | Description |
|---|---|---|
onShow |
function | เรียกก่อน modal แสดง |
onShown |
function | เรียกหลัง modal แสดงเสร็จ |
onHide |
function | เรียกก่อน modal ปิด |
onHidden |
function | เรียกหลัง modal ปิดเสร็จ |
ตัวอย่างการใช้ Options
const modal = new Modal({
id: 'confirm-modal',
title: 'ยืนยันการลบ',
content: '<p>คุณแน่ใจหรือไม่ที่จะลบรายการนี้?</p>',
width: '400px',
closeButton: true,
backdrop: true,
keyboard: true,
animation: true,
backdropOpacity: 0.7,
className: 'modal-danger',
onShow: function() {
console.log('Modal กำลังแสดง...');
},
onShown: function() {
console.log('Modal แสดงเสร็จแล้ว');
},
onHide: function() {
console.log('Modal กำลังปิด...');
},
onHidden: function() {
console.log('Modal ปิดเสร็จแล้ว');
}
});Data Binding
Modal รองรับการผูกข้อมูลกับ elements ภายใน modal ผ่าน data-modal-target attribute
การผูกข้อมูลพื้นฐาน
// สร้าง modal พร้อม data binding targets
const modal = new Modal({
title: 'ข้อมูลผู้ใช้',
content: `
<div class="user-profile">
<img data-modal-target="avatar" class="avatar">
<h3 data-modal-target="name"></h3>
<p data-modal-target="email"></p>
<p data-modal-target="bio"></p>
</div>
`
});
// ผูกข้อมูล
modal.bindData({
avatar: '/images/user.jpg',
name: 'สมชาย ใจดี',
email: 'somchai@example.com',
bio: 'นักพัฒนา Software'
});
// แสดง modal
modal.show();อัพเดทข้อมูล
// อัพเดทข้อมูลบางส่วน
modal.updateData({
bio: 'Senior Software Developer'
});
// หรือใช้ method chaining
modal.updateData({
name: 'สมชาย ใจดี (Admin)'
}).show();อ่านข้อมูลที่ผูกไว้
// อ่านข้อมูลทั้งหมด
const data = modal.getData();
console.log(data);
// { avatar: '/images/user.jpg', name: 'สมชาย ใจดี', ... }การผูกข้อมูลกับ Element Types ต่างๆ
Modal จะผูกข้อมูลตาม element type อัตโนมัติ:
const modal = new Modal({
content: `
<!-- Image: ตั้งค่า src -->
<img data-modal-target="productImage">
<!-- Link: ตั้งค่า href -->
<a data-modal-target="productLink">ดูสินค้า</a>
<!-- Input: ตั้งค่า value -->
<input data-modal-target="productName">
<!-- Text content (default) -->
<h3 data-modal-target="title"></h3>
<p data-modal-target="description"></p>
<!-- HTML content (ใช้ data-modal-html) -->
<div data-modal-target="richContent" data-modal-html></div>
`
});
modal.bindData({
productImage: '/images/product.jpg',
productLink: '/products/123',
productName: 'สินค้าตัวอย่าง',
title: 'สินค้าใหม่',
description: 'รายละเอียดสินค้า',
richContent: '<strong>HTML</strong> content'
});Content Sanitization
Modal จะ sanitize ข้อมูลอัตโนมัติเพื่อป้องกัน XSS:
// Text content - escape HTML อัตโนมัติ
modal.bindData({
name: '<script>alert("XSS")</script>' // จะแสดงเป็น text ธรรมดา
});
// HTML content - ลบ scripts และ event handlers
modal.bindData({
richContent: '<p onclick="alert()">Safe HTML</p>' // ลบ onclick ออก
});
// URL - validate protocols
modal.bindData({
link: 'javascript:alert()' // จะเปลี่ยนเป็น '#'
});Lifecycle Callbacks
Modal มี callbacks สำหรับแต่ละขั้นตอนของ lifecycle:
onShow - ก่อนแสดง Modal
const modal = new Modal({
title: 'Loading...',
content: '<div class="spinner"></div>',
onShow: function() {
console.log('Modal กำลังแสดง');
// เตรียมข้อมูล, เริ่ม loading
}
});onShown - หลังแสดง Modal เสร็จ
const modal = new Modal({
title: 'ข้อมูลผู้ใช้',
content: '<div id="user-data"></div>',
onShown: async function() {
console.log('Modal แสดงเสร็จแล้ว');
// โหลดข้อมูล, focus input
const data = await fetchUserData();
this.bindData(data);
}
});onHide - ก่อนปิด Modal
const modal = new Modal({
title: 'แบบฟอร์ม',
content: '<form>...</form>',
onHide: function() {
console.log('Modal กำลังปิด');
// บันทึกข้อมูล, ยืนยันการปิด
const hasUnsavedChanges = checkFormChanges();
if (hasUnsavedChanges) {
return confirm('มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ต้องการปิดหรือไม่?');
}
}
});onHidden - หลังปิด Modal เสร็จ
const modal = new Modal({
title: 'สำเร็จ',
content: '<p>บันทึกข้อมูลเรียบร้อย</p>',
onHidden: function() {
console.log('Modal ปิดเสร็จแล้ว');
// Cleanup, redirect, refresh data
window.location.reload();
}
});Accessibility
Modal มีฟีเจอร์ accessibility ครบถ้วน:
ARIA Attributes
// Modal จะตั้งค่า ARIA attributes อัตโนมัติ
const modal = new Modal({
title: 'ข้อมูล',
content: '<p>เนื้อหา</p>'
});
// จะสร้าง HTML ดังนี้:
// <div role="dialog" aria-modal="true" aria-hidden="false">
// ...
// </div>Inert Property
// Modal ใช้ inert property เพื่อป้องกัน interaction เมื่อซ่อน
modal.hide(); // modal.inert = true
modal.show(); // modal.inert = falseFocus Management
const modal = new Modal({
title: 'แบบฟอร์ม',
content: `
<input type="text" id="name">
<button>บันทึก</button>
`,
focus: true // เปิดใช้งาน focus management (default: true)
});
modal.show();
// จะ focus ที่ element แรกที่ focusable ได้อัตโนมัติFocus Trap
// Modal จะจับ focus ไว้ภายใน modal
// กด Tab จะวนไปที่ elements ใน modal เท่านั้น
// กด Shift+Tab จะวนกลับKeyboard Navigation
const modal = new Modal({
keyboard: true // เปิดใช้งาน keyboard support (default: true)
});
// กด ESC เพื่อปิด modalJavaScript API
Constructor
const modal = new Modal(options);Parameters:
options(Object) - ตัวเลือกการกำหนดค่า modal
Returns:
- Modal instance
show()
แสดง modal
modal.show();Returns: void
hide()
ปิด modal
modal.hide();Returns: void
setContent(content)
เปลี่ยนเนื้อหาของ modal
// HTML string
modal.setContent('<p>เนื้อหาใหม่</p>');
// DOM node
const element = document.createElement('div');
element.textContent = 'เนื้อหาใหม่';
modal.setContent(element);
// DocumentFragment
const fragment = document.createDocumentFragment();
// ... add nodes to fragment
modal.setContent(fragment);Parameters:
content(string|Node|DocumentFragment) - เนื้อหาใหม่
Returns: void
setTitle(title)
เปลี่ยนหัวข้อของ modal
modal.setTitle('หัวข้อใหม่');Parameters:
title(string) - หัวข้อใหม่
Returns: void
bindData(data)
ผูกข้อมูลกับ elements ใน modal
modal.bindData({
name: 'John Doe',
email: 'john@example.com'
});Parameters:
data(Object) - ข้อมูลที่ต้องการผูก
Returns: Modal instance (สำหรับ method chaining)
updateData(data)
อัพเดทข้อมูลที่ผูกไว้
modal.updateData({
email: 'newemail@example.com'
});Parameters:
data(Object) - ข้อมูลที่ต้องการอัพเดท
Returns: Modal instance (สำหรับ method chaining)
getData()
อ่านข้อมูลที่ผูกไว้
const data = modal.getData();
console.log(data);Returns: Object - ข้อมูลที่ผูกไว้
destroy()
ลบ modal ออกจาก DOM
modal.destroy();Returns: void
ตัวอย่างการใช้งาน
1. Modal แสดงข้อมูลผู้ใช้
// สร้าง modal
const userModal = new Modal({
id: 'user-modal',
title: 'ข้อมูลผู้ใช้',
content: `
<div class="user-profile">
<img data-modal-target="avatar" class="avatar">
<div class="user-info">
<h3 data-modal-target="name"></h3>
<p data-modal-target="email"></p>
<p data-modal-target="role"></p>
</div>
</div>
`,
width: '500px'
});
// ผูกข้อมูลและแสดง
userModal.bindData({
avatar: '/images/users/john.jpg',
name: 'John Doe',
email: 'john@example.com',
role: 'Administrator'
}).show();2. Modal ยืนยันการลบ
function confirmDelete(itemName, onConfirm) {
const modal = new Modal({
title: 'ยืนยันการลบ',
content: `
<p>คุณแน่ใจหรือไม่ที่จะลบ "<strong>${itemName}</strong>"?</p>
<p class="text-muted">การดำเนินการนี้ไม่สามารถย้อนกลับได้</p>
<div class="modal-actions">
<button class="btn btn-secondary" data-dismiss="modal">ยกเลิก</button>
<button class="btn btn-danger" id="confirm-delete">ลบ</button>
</div>
`,
width: '400px',
backdrop: true,
keyboard: true,
onShown: function() {
// Bind confirm button
document.getElementById('confirm-delete').addEventListener('click', () => {
onConfirm();
this.hide();
});
// Bind cancel button
document.querySelector('[data-dismiss="modal"]').addEventListener('click', () => {
this.hide();
});
}
});
modal.show();
}
// ใช้งาน
confirmDelete('รายการสินค้า #123', () => {
console.log('ลบรายการแล้ว');
// ทำการลบจริง
});3. Modal แสดงรูปภาพ
function showImageModal(imageUrl, title, description) {
const modal = new Modal({
title: title,
content: `
<div class="image-modal">
<img data-modal-target="image" class="modal-image">
<p data-modal-target="description" class="image-description"></p>
</div>
`,
width: '800px',
maxWidth: '90%',
className: 'image-modal-container'
});
modal.bindData({
image: imageUrl,
description: description
}).show();
}
// ใช้งาน
showImageModal(
'/images/product.jpg',
'สินค้าใหม่',
'รายละเอียดสินค้า...'
);4. Modal โหลดข้อมูลจาก API
async function showUserDetails(userId) {
// สร้าง modal พร้อม loading state
const modal = new Modal({
title: 'กำลังโหลด...',
content: '<div class="spinner">Loading...</div>',
width: '600px'
});
modal.show();
try {
// โหลดข้อมูล
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
// อัพเดทเนื้อหา
modal.setTitle('ข้อมูลผู้ใช้');
modal.setContent(`
<div class="user-details">
<img data-modal-target="avatar" class="avatar">
<h3 data-modal-target="name"></h3>
<p data-modal-target="email"></p>
<p data-modal-target="bio"></p>
</div>
`);
// ผูกข้อมูล
modal.bindData(user.data);
} catch (error) {
modal.setTitle('เกิดข้อผิดพลาด');
modal.setContent('<p class="error">ไม่สามารถโหลดข้อมูลได้</p>');
}
}
// ใช้งาน
showUserDetails(123);5. Modal แบบฟอร์ม
function showEditForm(userData) {
const modal = new Modal({
title: 'แก้ไขข้อมูล',
content: `
<form id="edit-form">
<div class="form-group">
<label>ชื่อ:</label>
<input type="text" name="name" data-modal-target="name" class="form-control">
</div>
<div class="form-group">
<label>อีเมล:</label>
<input type="email" name="email" data-modal-target="email" class="form-control">
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">บันทึก</button>
<button type="button" class="btn btn-secondary" data-dismiss>ยกเลิก</button>
</div>
</form>
`,
width: '500px',
onShown: function() {
const form = document.getElementById('edit-form');
// Bind form submit
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// บันทึกข้อมูล
await saveUserData(data);
this.hide();
});
// Bind cancel button
form.querySelector('[data-dismiss]').addEventListener('click', () => {
this.hide();
});
}
});
// ผูกข้อมูลเริ่มต้น
modal.bindData(userData).show();
}
// ใช้งาน
showEditForm({
name: 'John Doe',
email: 'john@example.com'
});API Reference
Properties
| Property | Type | Description |
|---|---|---|
id |
string | ID ของ modal element |
modal |
HTMLElement | Modal element |
dialog |
HTMLElement | Modal dialog element |
header |
HTMLElement | Modal header element |
body |
HTMLElement | Modal body element |
visible |
boolean | สถานะการแสดงผล |
boundData |
Object | ข้อมูลที่ผูกไว้ |
backdropId |
string|null | ID ของ backdrop |
Methods
Constructor
new Modal(options)Public Methods
show()- แสดง modalhide()- ปิด modalsetContent(content)- เปลี่ยนเนื้อหาsetTitle(title)- เปลี่ยนหัวข้อbindData(data)- ผูกข้อมูลupdateData(data)- อัพเดทข้อมูลgetData()- อ่านข้อมูลdestroy()- ลบ modal
Private Methods
createModal()- สร้าง modal DOMbindEvents()- ผูก event handlers_cleanupModalElements()- ทำความสะอาด elements_setElementContent(element, value, key)- ตั้งค่า element content_sanitizeValue(value, context)- Sanitize ค่า_sanitizeText(text)- Sanitize text_sanitizeHTML(html)- Sanitize HTML_sanitizeURL(url)- Sanitize URL
แนวทางปฏิบัติที่ดี
✅ ควรทำ
1. ใช้ Data Binding แทนการตั้งค่าโดยตรง
// ✅ ดี - ใช้ data binding
modal.bindData({
name: userName,
email: userEmail
});
// ❌ ไม่ดี - ตั้งค่าโดยตรง
modal.body.querySelector('.name').textContent = userName;
modal.body.querySelector('.email').textContent = userEmail;2. ใช้ Lifecycle Callbacks
// ✅ ดี - ใช้ callbacks
const modal = new Modal({
content: '<div id="data"></div>',
onShown: async function() {
const data = await fetchData();
this.bindData(data);
}
});
// ❌ ไม่ดี - โหลดข้อมูลก่อนแสดง modal
const data = await fetchData();
modal.bindData(data);
modal.show();3. ทำความสะอาดเมื่อไม่ใช้แล้ว
// ✅ ดี - destroy เมื่อไม่ใช้
modal.destroy();
// ❌ ไม่ดี - ทิ้งไว้ใน DOM
// modal ยังคงอยู่ใน memory4. ใช้ Method Chaining
// ✅ ดี - chain methods
modal.bindData(userData).show();
// ❌ ไม่ดี - แยกบรรทัด
modal.bindData(userData);
modal.show();❌ ไม่ควรทำ
1. อย่าใส่ HTML ที่ไม่ปลอดภัยโดยตรง
// ❌ อันตราย - XSS vulnerability
modal.setContent(`<p>${userInput}</p>`);
// ✅ ปลอดภัย - ใช้ data binding
modal.setContent('<p data-modal-target="message"></p>');
modal.bindData({ message: userInput });2. อย่าสร้าง Modal หลายตัวซ้อนกัน
// ❌ ไม่ดี - modal ซ้อน modal
modal1.show();
modal2.show(); // จะทับกัน
// ✅ ดี - ปิดก่อนเปิดใหม่
modal1.hide();
setTimeout(() => modal2.show(), 200);3. อย่าลืมจัดการ Event Listeners
// ❌ ไม่ดี - memory leak
const modal = new Modal({
onShown: function() {
document.getElementById('btn').addEventListener('click', handler);
// ไม่มีการ remove listener
}
});
// ✅ ดี - cleanup listeners
const modal = new Modal({
onShown: function() {
const btn = document.getElementById('btn');
btn.addEventListener('click', handler);
},
onHidden: function() {
const btn = document.getElementById('btn');
btn.removeEventListener('click', handler);
}
});💡 Tips
1. ใช้ CSS Classes สำหรับ Styling
const modal = new Modal({
className: 'modal-large modal-primary',
content: '...'
});2. ใช้ Template Literals สำหรับ Content ที่ซับซ้อน
const modal = new Modal({
content: `
<div class="complex-content">
<section>...</section>
<section>...</section>
</div>
`
});3. เก็บ Modal Instance ไว้ใช้ซ้ำ
// สร้างครั้งเดียว
const confirmModal = new Modal({
title: 'ยืนยัน',
content: '<p data-modal-target="message"></p>'
});
// ใช้ซ้ำได้
function confirm(message) {
confirmModal.updateData({ message }).show();
}เพิ่มเติม
- ModalDataBinder - ใช้ modal กับ data binding แบบ declarative
- BackdropManager - จัดการ backdrop
- DialogManager - จัดการ dialogs ทั่วไป