javascriptjquerytwitter-bootstrapbootstrap-modal

Make Bootstrap non-closable modal closable again


I'm using this code to make a Bootstrap 3.4.1 modal non-closable -- to not allow the user to click outside the modal or use Esc to close it, while still allowing the normal .modal("hide") and data-dismiss="modal" to work:

$('#myModal').removeData('bs.modal')
    .modal({
        backdrop: 'static',
        keyboard: false
    });

This is from this comment on an answer: Disallow Twitter Bootstrap modal window from closing

However, I'm struggling to reverse the above. Just passing in the opposite options won't work; the modal remains non-closable:

$("#myModal").modal({
    backdrop: true,
    keyboard: true
});

With help from the SO JavaScript chat (thanks @matt), I also tried using the hide.bs.modal handler, wherein I'd read a data-allow-close attribute within to determine if I should allow closing via keyboard/mouse:

attachNonClosableModalHideHandler: function() {
    const $self = $(this);
    $("#myModal").on("hide.bs.modal", $self.handleNonClosableModalHide);
},

handleNonClosableModalHide: function () {
    return $(this).data("allow-close");
}

Here's the modal's main DIV tag:

<div id="myModal" class="modal default fade" tabindex="-1" 
     role="dialog" data-allow-close="true">...</div>

However, the above handler prevents calls to $("#myModal").modal("hide") from working because I now need to set the allow-close data attribute before doing so. Also, the data-dismiss="modal" buttons within the modal won't work for the same reason.

I created this jQuery plugin to call as a wrapper for hiding the modal but it's all becoming cumbersome:

$.fn.hideNonClosableModal = function () {
    const $self = $(this);
    const hasClass = $self.hasClass("modal");
    const hasRole = $self.attr("role") === "dialog";
    if (!hasClass || !hasRole) {
        return this;
    }

    $self.data("allow-close", true);
    $self.modal("hide");

    return this;
};

It shouldn't be this difficult to make a modal closable again. What am I doing wrong? Thanks.


Solution

  • With great help from (and thanks to) this answer, here's a solution for Bootstrap 3.x:

    To make the modal non-closable:

    $("#myModal").modal("hide");
    $("#myModal").data("bs.modal").options.backdrop = 'static';
    $("#myModal").off("keydown.dismiss.bs.modal");
    $("#myModal").data("bs.modal").options.keyboard = false;
    

    To make it closable:

    $("#myModal").modal("hide");
    $("#myModal").data("bs.modal").options.backdrop = true;
    $("#myModal").data("bs.modal").escape(); // Resets the keyboard.
    

    We need to call $("#myModal").modal("hide"); to initialize the modal and make the options available. We can also check data("bs.modal") to determine if modal's initialized (see below).

    Here it is in a jQuery plugin:

    $.fn.toggleModalClosable = function (options) {
        // Options can be reduced to a bool argument.
        const settings = $.extend({
            isClosable: true
        }, options);
    
        const $self = $(this);
        // If this isn't a modal, return this.
        if (!$self.hasClass("modal") || $self.attr("role") !== "dialog"){ 
            return this;
        }
    
        // If modal's not initialized, initialize it by hiding it.
        // If we've shown or hidden the modal, it's been initialized.
        if (!$self.data("bs.modal")) {
            $self.modal("hide");
        }
    
        if (options.isClosable) {
            $self.data("bs.modal").options.backdrop = true;
            $self.data("bs.modal").escape();
            $(".mymodal-close-button").show();
        } else {
            $self.data("bs.modal").options.backdrop = "static";
            $self.off("keydown.dismiss.bs.modal");
            $self.data("bs.modal").options.keyboard = false;
            $(".mymodal-close-button").hide();
        }
    
        return this;
    };