Now.js Framework Documentation
Modal - Modal Dialog System
Modal - Modal Dialog System
Complete documentation for the Modal class - a feature-rich component for creating and managing modal dialogs with animation, accessibility, and data binding support.
📋 Table of Contents
- Overview
- Installation
- Basic Usage
- Configuration Options
- Data Binding
- Lifecycle Callbacks
- Accessibility
- JavaScript API
- Examples
- API Reference
- Best Practices
Overview
Modal is a class for creating feature-rich modal dialogs with overlay display, backdrop, animation, keyboard navigation, and data binding capabilities.
Key Features
- ✅ Programmatic Creation: Create modals with JavaScript instantly
- ✅ Animation Support: Built-in fade-in/fade-out animations
- ✅ Backdrop Management: Automatic backdrop via
BackdropManager - ✅ Keyboard Support: Close with ESC key
- ✅ Focus Management: Automatic focus and focus trap
- ✅ Accessibility: ARIA attributes and
inertproperty support - ✅ Data Binding: Bind data to modal elements
- ✅ Content Sanitization: XSS protection with automatic sanitization
- ✅ Lifecycle Callbacks: Hooks for each lifecycle stage
- ✅ Element Cleanup: Automatic cleanup when closing
When to Use Modal
✅ Use Modal when:
- Creating modals programmatically
- Need animation and full accessibility
- Need to bind data to modal content
- Want JavaScript API control
❌ Don't use Modal when:
- Modal elements already exist in HTML (use
ModalDataBinderinstead) - Need simple modals without special features
- Don't want backdrop
Installation
Modal is included with Now.js Framework and available via window object:
// No import needed - ready to use
console.log(window.Modal); // Modal classDependencies
- BackdropManager – For backdrop management (optional)
- FormManager – For form cleanup in modals (optional)
- ElementManager – For element cleanup in modals (optional)
Basic Usage
1. Simple Modal
// Create modal instance
const modal = new Modal({
title: 'Welcome',
content: '<p>This is your first modal!</p>'
});
// Show modal
modal.show();2. Modal with Options
const modal = new Modal({
id: 'my-modal',
title: 'User Information',
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. Close Modal
// Close modal
modal.hide();
// Or press ESC key
// Or click backdrop (if enabled)Configuration Options
Basic Options
| Option | Type | Default | Description |
|---|---|---|---|
id |
string | auto-generated | Modal element ID |
title |
string | '' |
Modal title |
content |
string|Node | '' |
Modal content (HTML string or DOM node) |
width |
string | 'auto' |
Modal width |
maxWidth |
string | '90%' |
Maximum width |
height |
string | 'auto' |
Modal height |
maxHeight |
string | '90vh' |
Maximum height |
className |
string | '' |
Additional CSS classes |
Display Options
| Option | Type | Default | Description |
|---|---|---|---|
closeButton |
boolean | true |
Show close button |
animation |
boolean | true |
Enable animations |
backdrop |
boolean | true |
Close on backdrop click |
keyboard |
boolean | true |
Close on ESC key |
focus |
boolean | true |
Auto focus management |
Backdrop Options
| Option | Type | Default | Description |
|---|---|---|---|
backdropOpacity |
number | 0.5 |
Backdrop opacity |
backdropColor |
string | 'rgba(0,0,0,0.5)' |
Backdrop color |
Lifecycle Callbacks
| Option | Type | Description |
|---|---|---|
onShow |
function | Called before modal shows |
onShown |
function | Called after modal shown |
onHide |
function | Called before modal hides |
onHidden |
function | Called after modal hidden |
Data Binding
Modal supports data binding to elements via data-modal-target attribute:
Basic Data Binding
// Create modal with data binding targets
const modal = new Modal({
title: 'User Profile',
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>
`
});
// Bind data
modal.bindData({
avatar: '/images/user.jpg',
name: 'John Doe',
email: 'john@example.com',
bio: 'Software Developer'
});
// Show modal
modal.show();Update Data
// Update partial data
modal.updateData({
bio: 'Senior Software Developer'
});
// Or use method chaining
modal.updateData({
name: 'John Doe (Admin)'
}).show();Get Bound Data
// Get all bound data
const data = modal.getData();
console.log(data);
// { avatar: '/images/user.jpg', name: 'John Doe', ... }Element Type Binding
Modal automatically binds data based on element type:
const modal = new Modal({
content: `
<!-- Image: sets src -->
<img data-modal-target="productImage">
<!-- Link: sets href -->
<a data-modal-target="productLink">View Product</a>
<!-- Input: sets value -->
<input data-modal-target="productName">
<!-- Text content (default) -->
<h3 data-modal-target="title"></h3>
<p data-modal-target="description"></p>
<!-- HTML content (use data-modal-html) -->
<div data-modal-target="richContent" data-modal-html></div>
`
});
modal.bindData({
productImage: '/images/product.jpg',
productLink: '/products/123',
productName: 'Sample Product',
title: 'New Product',
description: 'Product details',
richContent: '<strong>HTML</strong> content'
});Lifecycle Callbacks
onShow - Before Modal Shows
const modal = new Modal({
title: 'Loading...',
content: '<div class="spinner"></div>',
onShow: function() {
console.log('Modal is showing');
// Prepare data, start loading
}
});onShown - After Modal Shown
const modal = new Modal({
title: 'User Data',
content: '<div id="user-data"></div>',
onShown: async function() {
console.log('Modal shown');
// Load data, focus input
const data = await fetchUserData();
this.bindData(data);
}
});onHide - Before Modal Hides
const modal = new Modal({
title: 'Form',
content: '<form>...</form>',
onHide: function() {
console.log('Modal is hiding');
// Save data, confirm close
const hasUnsavedChanges = checkFormChanges();
if (hasUnsavedChanges) {
return confirm('You have unsaved changes. Close anyway?');
}
}
});onHidden - After Modal Hidden
const modal = new Modal({
title: 'Success',
content: '<p>Data saved successfully</p>',
onHidden: function() {
console.log('Modal hidden');
// Cleanup, redirect, refresh data
window.location.reload();
}
});Accessibility
Modal includes comprehensive accessibility features:
ARIA Attributes
// Modal sets ARIA attributes automatically
const modal = new Modal({
title: 'Information',
content: '<p>Content</p>'
});
// Creates HTML like:
// <div role="dialog" aria-modal="true" aria-hidden="false">
// ...
// </div>Inert Property
// Modal uses inert property to prevent interaction when hidden
modal.hide(); // modal.inert = true
modal.show(); // modal.inert = falseFocus Management
const modal = new Modal({
title: 'Form',
content: `
<input type="text" id="name">
<button>Save</button>
`,
focus: true // Enable focus management (default: true)
});
modal.show();
// Automatically focuses first focusable elementFocus Trap
// Modal traps focus within modal
// Tab cycles through modal elements only
// Shift+Tab cycles backwardsKeyboard Navigation
const modal = new Modal({
keyboard: true // Enable keyboard support (default: true)
});
// Press ESC to close modalJavaScript API
Constructor
const modal = new Modal(options);Parameters:
options(Object) - Modal configuration options
Returns:
- Modal instance
show()
Show the modal
modal.show();Returns: void
hide()
Hide the modal
modal.hide();Returns: void
setContent(content)
Change modal content
// HTML string
modal.setContent('<p>New content</p>');
// DOM node
const element = document.createElement('div');
element.textContent = 'New content';
modal.setContent(element);
// DocumentFragment
const fragment = document.createDocumentFragment();
// ... add nodes to fragment
modal.setContent(fragment);Parameters:
content(string|Node|DocumentFragment) - New content
Returns: void
setTitle(title)
Change modal title
modal.setTitle('New Title');Parameters:
title(string) - New title
Returns: void
bindData(data)
Bind data to modal elements
modal.bindData({
name: 'John Doe',
email: 'john@example.com'
});Parameters:
data(Object) - Data to bind
Returns: Modal instance (for method chaining)
updateData(data)
Update bound data
modal.updateData({
email: 'newemail@example.com'
});Parameters:
data(Object) - Data to update
Returns: Modal instance (for method chaining)
getData()
Get bound data
const data = modal.getData();
console.log(data);Returns: Object - Bound data
destroy()
Remove modal from DOM
modal.destroy();Returns: void
Examples
1. User Profile Modal
// Create modal
const userModal = new Modal({
id: 'user-modal',
title: 'User Profile',
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'
});
// Bind data and show
userModal.bindData({
avatar: '/images/users/john.jpg',
name: 'John Doe',
email: 'john@example.com',
role: 'Administrator'
}).show();2. Confirmation Modal
function confirmDelete(itemName, onConfirm) {
const modal = new Modal({
title: 'Confirm Delete',
content: `
<p>Are you sure you want to delete "<strong>${itemName}</strong>"?</p>
<p class="text-muted">This action cannot be undone</p>
<div class="modal-actions">
<button class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button class="btn btn-danger" id="confirm-delete">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();
}
// Usage
confirmDelete('Product #123', () => {
console.log('Item deleted');
// Perform actual deletion
});3. Image 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();
}
// Usage
showImageModal(
'/images/product.jpg',
'New Product',
'Product details...'
);4. API Data Modal
async function showUserDetails(userId) {
// Create modal with loading state
const modal = new Modal({
title: 'Loading...',
content: '<div class="spinner">Loading...</div>',
width: '600px'
});
modal.show();
try {
// Load data
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
// Update content
modal.setTitle('User Information');
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>
`);
// Bind data
modal.bindData(user.data);
} catch (error) {
modal.setTitle('Error');
modal.setContent('<p class="error">Failed to load data</p>');
}
}
// Usage
showUserDetails(123);5. Form Modal
function showEditForm(userData) {
const modal = new Modal({
title: 'Edit Information',
content: `
<form id="edit-form">
<div class="form-group">
<label>Name:</label>
<input type="text" name="name" data-modal-target="name" class="form-control">
</div>
<div class="form-group">
<label>Email:</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">Save</button>
<button type="button" class="btn btn-secondary" data-dismiss>Cancel</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);
// Save data
await saveUserData(data);
this.hide();
});
// Bind cancel button
form.querySelector('[data-dismiss]').addEventListener('click', () => {
this.hide();
});
}
});
// Bind initial data
modal.bindData(userData).show();
}
// Usage
showEditForm({
name: 'John Doe',
email: 'john@example.com'
});API Reference
Properties
| Property | Type | Description |
|---|---|---|
id |
string | Modal element ID |
modal |
HTMLElement | Modal element |
dialog |
HTMLElement | Modal dialog element |
header |
HTMLElement | Modal header element |
body |
HTMLElement | Modal body element |
visible |
boolean | Visibility state |
boundData |
Object | Bound data |
backdropId |
string|null | Backdrop ID |
Methods
Constructor
new Modal(options)Public Methods
show()- Show modalhide()- Hide modalsetContent(content)- Change contentsetTitle(title)- Change titlebindData(data)- Bind dataupdateData(data)- Update datagetData()- Get datadestroy()- Remove modal
Private Methods
createModal()- Create modal DOMbindEvents()- Bind event handlers_cleanupModalElements()- Cleanup elements_setElementContent(element, value, key)- Set element content_sanitizeValue(value, context)- Sanitize value_sanitizeText(text)- Sanitize text_sanitizeHTML(html)- Sanitize HTML_sanitizeURL(url)- Sanitize URL
Best Practices
✅ Do
1. Use Data Binding Instead of Direct Manipulation
// ✅ Good - use data binding
modal.bindData({
name: userName,
email: userEmail
});
// ❌ Bad - direct manipulation
modal.body.querySelector('.name').textContent = userName;
modal.body.querySelector('.email').textContent = userEmail;2. Use Lifecycle Callbacks
// ✅ Good - use callbacks
const modal = new Modal({
content: '<div id="data"></div>',
onShown: async function() {
const data = await fetchData();
this.bindData(data);
}
});
// ❌ Bad - load before showing
const data = await fetchData();
modal.bindData(data);
modal.show();3. Clean Up When Done
// ✅ Good - destroy when done
modal.destroy();
// ❌ Bad - leave in DOM
// modal still in memory4. Use Method Chaining
// ✅ Good - chain methods
modal.bindData(userData).show();
// ❌ Bad - separate lines
modal.bindData(userData);
modal.show();❌ Don't
1. Don't Insert Unsafe HTML Directly
// ❌ Dangerous - XSS vulnerability
modal.setContent(`<p>${userInput}</p>`);
// ✅ Safe - use data binding
modal.setContent('<p data-modal-target="message"></p>');
modal.bindData({ message: userInput });2. Don't Stack Multiple Modals
// ❌ Bad - stacked modals
modal1.show();
modal2.show(); // Will overlap
// ✅ Good - close before opening new
modal1.hide();
setTimeout(() => modal2.show(), 200);3. Don't Forget Event Listener Cleanup
// ❌ Bad - memory leak
const modal = new Modal({
onShown: function() {
document.getElementById('btn').addEventListener('click', handler);
// No listener removal
}
});
// ✅ Good - 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. Use CSS Classes for Styling
const modal = new Modal({
className: 'modal-large modal-primary',
content: '...'
});2. Use Template Literals for Complex Content
const modal = new Modal({
content: `
<div class="complex-content">
<section>...</section>
<section>...</section>
</div>
`
});3. Reuse Modal Instances
// Create once
const confirmModal = new Modal({
title: 'Confirm',
content: '<p data-modal-target="message"></p>'
});
// Reuse
function confirm(message) {
confirmModal.updateData({ message }).show();
}See Also
- ModalDataBinder - Declarative modal data binding
- BackdropManager - Backdrop management
- DialogManager - General dialog management