Max number of modals

If you want to display the details of an item listed on a card, you can use a single modal like this:

<div class="modal fade" id="cardModal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="modalTitle"></h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div class="modal-body" id="modalBody"></div>
    </div>
  </div>
</div>

For the cards, you can add data attributes:

<div class="card" data-title="Card 1" data-content="Details for Card 1" onclick="openModal(this)">
  <div class="card-body">Card 1</div>
</div>

<div class="card" data-title="Card 2" data-content="Details for Card 2" onclick="openModal(this)">
  <div class="card-body">Card 2</div>
</div>

Finally, use a bit of JavaScript to make it work:

<script>
function openModal(card) {
  document.getElementById('modalTitle').textContent = card.dataset.title;
  document.getElementById('modalBody').textContent = card.dataset.content;
  new bootstrap.Modal(document.getElementById('cardModal')).show();
}
</script>

This is similar to the method I used when creating the YouTubePlayer extension for Wappler.

Edit: In fact, this would make a great extension or even a custom element

Custom Element: <card-with-modal>

HTML Usage

<card-with-modal title="Card 1" content="Details for Card 1"></card-with-modal>
<card-with-modal title="Card 2" content="Details for Card 2"></card-with-modal>
<!-- Repeat as needed -->

JavaScript Definition:

<script>
class CardWithModal extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    const title = this.getAttribute('title') || 'Untitled';
    const content = this.getAttribute('content') || 'No content provided';

    this.shadowRoot.innerHTML = `
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
      <div class="card m-2" style="cursor:pointer;">
        <div class="card-body">
          <h5 class="card-title">${title}</h5>
          <button class="btn btn-primary" id="openBtn">Open</button>
        </div>
      </div>

      <div class="modal fade" id="modal-${title.replace(/\s+/g, '-')}" tabindex="-1">
        <div class="modal-dialog">
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title">${title}</h5>
              <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body">${content}</div>
          </div>
        </div>
      </div>
    `;

    const modalId = `modal-${title.replace(/\s+/g, '-')}`;
    const openBtn = this.shadowRoot.getElementById('openBtn');

    openBtn.addEventListener('click', () => {
      const modal = new bootstrap.Modal(this.shadowRoot.getElementById(modalId));
      modal.show();
    });
  }
}

customElements.define('card-with-modal', CardWithModal);
</script>

Notes for Integration

  • This uses Shadow DOM, so Bootstrap styles are scoped. If you want global styling or animations, you can move the modal outside the shadow root or use.
  • You can extend this to accept images, icons, or even fetch content dynamically.
  • For performance, you might consider lazy-loading modal content if your cards get more complex.

@George, nudge, nudge!