Fenêtre modale Boostrap 5 chargée en Ajax

Lorsqu’on a besoin d’ouvrir une fenêtre modale Bootstrap mais qu’on ne souhaite pas que tout son contenu soit chargé au préalable sur la page, il peut être intéressant de mettre en place un chargement en AJAX.

L’idée est la suivante : on se base sur l’attribut data-modal-href des liens pour écouter les événements de clic ; on injecte le code HTML de la fenêtre modale « vide » dans la page ; on charge le contenu en AJAX.

Comme Bootstrap 5 ne dépend plus de jQuery, nous allons écrire ce système en JavaScript natif.

Dans le code de la page principale, le lien destiné à ouvrir la modale sera de la forme suivante :

<a href="#" data-modal-href="modal-content.html">Ouvrir la modale</a>

Dans notre script, il faudra écouter les clics sur ces liens, afin de déclencher l’ouverture de la fenêtre modale. Ici, le clic déclenche la fonction getAjaxModalElement, qui est chargée de créer l’élément HTML destiné à accueillir le contenu charger en Ajax s’il n’existe pas déjà sur le document. On instancie ensuite un objet Modal fourni par Bootstrap, qui contiendra notre code HTML par défaut, indiquant le chargement en cours.

Ensuite, nous lançons le fetch vers l’URL de l’attribut data-modal-href du lien, et nous injectons la réponse HTML dans la fenêtre modale déjà affichée.

import { Modal } from 'bootstrap'

function getAjaxModalElement(){

    let modalDiv = document.getElementById('pleb_ajax_modal');
    
    if(!modalDiv){
        modalDiv = document.createElement('div');
        modalDiv.setAttribute('id', 'pleb_ajax_modal');
        modalDiv.setAttribute('class', 'modal fade');
        modalDiv.setAttribute('tabindex', '-1');
        document.body.appendChild(modalDiv);
    }

    const modalHtml = `
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="exampleModalLabel">Chargement</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body">
                <div class="text-center">
                    <i class="fa fa-fw fa-spin fa-cog"></i> Chargement...
                </div>
            </div>
        </div>
    </div>`;
    modalDiv.innerHTML = modalHtml;

    return modalDiv;
}

document.querySelectorAll("a[data-modal-href]").forEach(modalLink => 
    modalLink.addEventListener("click", (e) => {
        e.preventDefault();

        const modalDiv = getAjaxModalElement();

        const ajaxModal = new Modal(modalDiv, {});
        ajaxModal.show();

        const modalHref = modalLink.getAttribute('data-modal-href');

        fetch(modalHref, {
            method: 'GET',
        }).then((response) => {
            if (!response.ok) throw Error(response.statusText);
            return response.text();

        }).then(function(html){
            const modalContent = modalDiv.querySelector('.modal-dialog');
            modalContent.innerHTML = html;

        }).catch((error) => {
            console.error(error);
        });

    })
)

Ci-dessous un exemple du code HTML que devra renvoyer la page chargée en AJAX modal-content.html. Notez qu’il ne faut fournir que les balises à partir du niveau .modal-content, car les parents .modal-dialog et .modal sont gérés depuis le JavaScript.

<div class="modal-content">
    <div class="modal-header">
        <h5 class="modal-title">Titre</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
    </div>
    <div class="modal-body">
        <p>Le contenu</p>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
        <button type="button" class="btn btn-primary">Enregistrer</button>
    </div>
</div>