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.