Now.js Framework Documentation

Now.js Framework Documentation

Modal - Modal Dialog System

EN 27 Nov 2025 12:15

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

  1. Overview
  2. Installation
  3. Basic Usage
  4. Configuration Options
  5. Data Binding
  6. Lifecycle Callbacks
  7. Accessibility
  8. JavaScript API
  9. Examples
  10. API Reference
  11. 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 inert property 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 ModalDataBinder instead)
  • 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 class

Dependencies

  • 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 = false

Focus 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 element

Focus Trap

// Modal traps focus within modal
// Tab cycles through modal elements only
// Shift+Tab cycles backwards

Keyboard Navigation

const modal = new Modal({
  keyboard: true // Enable keyboard support (default: true)
});

// Press ESC to close modal

JavaScript 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 modal
  • hide() - Hide modal
  • setContent(content) - Change content
  • setTitle(title) - Change title
  • bindData(data) - Bind data
  • updateData(data) - Update data
  • getData() - Get data
  • destroy() - Remove modal

Private Methods

  • createModal() - Create modal DOM
  • bindEvents() - 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 memory

4. 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