Bootstrap modal - hide one then show another
Asked Answered
C

6

6

I've been using jQueryUI for a long time now but have recently made the switch to Bootstrap for aesthetic reasons. I am now struggling with what I would expect to be a simple issue and wondered if it's something that others more familiar with Bootstrap can help me with.

I have a generic function for creating dialogs on-the-fly and there are occasions where I show a dialog with no buttons (when processing something), and then swap it to a dialog that does have buttons (process complete - click OK, for example). I'm not trying to define a set process here so I'm basically saying I want to be able to close one dialog and open another whenever needed. This is where the problem comes in.

With Bootstrap the dialogs animate in and out, and I like that and want to keep it. I don't want to do it when swapping dialogs though. I can do this by removing the class fade from the first dialog when it shows, and from the second dialog before it shows, and that works great. I then add the class to the second dialog so that it will animate out. However, the animation goes wrong when I do this and there's an ugly flash where the background div should fade out gently.

I've put together a jsfiddle to demonstrate the issue. You can click the close button on the first dialog to see what the fade out animation should look like.

Any help would be appreciated before I start digging into the Bootstrap source files.

http://jsfiddle.net/ArchersFiddle/0302gLav/1/

tl;dr

Look at the jsfiddle - click "show dialog 2" - click "ok". I want to get rid of the black flash at the end.

CSS

@import url("//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css");
.modal {
    display: none;
}

HTML

<div id="dialog1" class="modal fade">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Modal Dialog 1</h4>
      </div>
      <div class="modal-body">This is the first modal dialog</div>
      <div class="modal-footer">
        <button type="button" id="dialog-ok" class="btn btn-default">Show dialog 2</button>          
        <button type="button" id="dialog-close" class="btn btn-default" data-dismiss="modal">Close</button>          
      </div>
    </div>
  </div>
</div>

<div id="dialog2" class="modal">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">Modal Dialog 2</h4>
      </div>
      <div class="modal-body">This is the second modal dialog</div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">OK</button>          
      </div>
    </div>
  </div>
</div>

JavaScript

function showDialog2() {
    $("#dialog1").removeClass("fade").modal("hide");
    $("#dialog2").modal("show").addClass("fade");
}

$("#dialog1").modal("show");

$("#dialog-ok").on("click", function() {
    showDialog2();
});
Centner answered 7/8, 2014 at 12:25 Comment(0)
C
3

Okay, I don't like to answer my own question, but I've got a solution that is 100% foolproof (as far as I can tell). I've ended up going for a solution that checks for an existing dialog and modifies that, rather than hiding it and showing a new one.

Here's a working jsfiddle (using echo in the ajax call where it normally loads a html template)...

http://jsfiddle.net/ArchersFiddle/0302gLav/9/

The code is part of a larger library I'm working on, but I'll post it here anyway as it may well prove useful to others.

JavaScript Library

(function () {

    var _defaultOptions = {
        backdrop: "static",
        close: true,
        keyboard: true
    };

    window.bootstrap = {
        modal: {
            show: function (options) {

                options = $.extend(_defaultOptions, options);

                var buttonText = "";

                for (var key in options.buttons) {

                    options.buttons[key].id = "button_" + key.split(" ").join("");

                    var button = options.buttons[key];

                    buttonText += "<button type=\"button\" " +
                        "id=\"" + button.id + "\" " +
                        "class=\"btn " +
                        (typeof (button.class) == "undefined" ? "btn-default" : button.class) + "\" " +
                        (typeof (button.dismiss) == "undefined" ? "" : "data-dismiss=\"modal\" ") + ">" +
                        key + "</button>";
                }

                $.ajax({
                    url: "templates/bootstrap-modal.html"
                })
                .done(function (data) {
                    data = data
                        .replace("{:Title}", options.title)
                        .replace("{:Body}", options.body)
                        .replace("{:Buttons}", buttonText);

                    var $modal = $("#bootstrap-modal");
                    var existing = $modal.length;

                    if (existing) {
                        $modal.html($(data).find(".modal-dialog"));
                    }
                    else {
                        $modal = $(data).appendTo("body");
                    }

                    for (var key in options.buttons) {
                        var button = options.buttons[key];
                        if (typeof (button.callback) !== undefined) {
                            $("#" + button.id).on("click", button.callback);
                        }
                    }

                    $modal.off("shown.bs.modal").on("shown.bs.modal", function(e) {
                        if (typeof(options.onshow) === "function") {
                            options.onshow(e);
                        }
                    });

                    if (!existing) {
                        $modal.modal(options);
                    }

                    if (options.large === true) {
                        $modal.find(".modal-dialog").addClass("modal-lg");
                    }

                    if (options.close === false) {
                        $modal.find("button.close").remove();
                    }
                });
            },
            hide: function (callback) {
                var $modal = $("#bootstrap-modal");

                if (!$modal.length) return;

                $modal.on("hidden.bs.modal", function(e) {
                    $modal.remove();
                    if (typeof(callback) === "function") {
                        callback(e);
                    }
                });

                $modal.modal("hide");
            }
        }
    };
})();

Template HTML

<div id="bootstrap-modal" class="modal fade">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
        <h4 class="modal-title">{:Title}</h4>
      </div>
      <div class="modal-body">{:Body}</div>
      <div class="modal-footer">
        {:Buttons}
      </div>
    </div>
  </div>
</div>

Example usage:

bootstrap.modal.show({
    title: "Dialog Title",
    body: "<p>This is the dialog body and can contain any old html rubbish.</p>",
    buttons: {
        Close: {
            dismiss: true
        }
    }
});

Further options explained

bootstrap.modal.show({
    backdrop: true,                     // show the backdrop
    close: true,                        // show the close button
    keyboard: true,                     // allow the keyboard to close the dialog
    title: "Dialog Title",
    body: "<p>This is the dialog body and can contain any old html rubbish.</p>",
    buttons: {
        Button1: {
            class: "btn-primary",           // any class you want to add to the button
            dismiss: false,                 // does this button close the dialog?
            callback: function() {          // button click handler
                // the button was clicked - do something here
            }
        },
        Button2: {
            // options as defined as above.  You can have as many buttons as you want
        }
    },
    onshow: function(e) {
        // this will trigger when the dialog has completed the show animation
    }
});

and

bootstrap.modal.hide(function(e) {
    // this will trigger when the dialog has completed the hide animation
});

All the options in the show() method are optional, but obviously you'll want a title and a body.

Centner answered 7/8, 2014 at 14:18 Comment(0)
I
4
function showDialog2() {
$("#dialog1").removeClass("fade").modal("hide");
$("#dialog2").addClass("fade").modal("show");

}

you want to be this

Immediately answered 7/8, 2014 at 12:40 Comment(2)
Thanks, but there is a blatant animation between the 2 dialogs. If you look at my jsfiddle you will see that there isn't.Centner
removing 'fade' class worked for me, there is a bug in this fiddle, fade class must be removed from the second dialog too, instead of adding itPurely
C
4

UPDATED:

I added a click() handler for your last button with an added test identifier id="test" where the dialog and the background gets faded out with the fadeOut() effect. The important thing is to fade out the element .modal-backdrop which encapsules both the dialog and background:

$("#test").on("click", function () {
    $(".modal-backdrop").fadeOut("slow");
});

JsFiddle

Chauncey answered 7/8, 2014 at 13:9 Comment(5)
Thanks, that's not actually that bad. I want to leave it using the in-built animation that comes with Bootstrap though. For example, the class in is what handles the animation of the dialog and in future that may be changed to allow other options, so I don't want to hard-code anything that is currently done by Bootstrap. See here for an example... jsfiddle.net/ArchersFiddle/fvr5Lpnn/2Centner
I like what you did with the removeClass("in"); but I do understand how you are handling the dialogs and why hardcoding something is not really the clever alternative. That is what I tried not doing in the first place but it seems when you override default functionality, the flickering occurs.Chauncey
Exactly my problem, yes. The solution I have in place at the moment is pretty much this answer (with the use of the in class). I'm trying to keep it as generic as possible as it's part of a web product that is live on over 40 customer sites. Whenever new options are added to the controls used in the sites I expose these options to the customers via their site admin pages. It will be a lot simpler for me to use the existing Bootstrap functionality if at a later date I simply need to change a class from in to bounce, for example. I do appreciate the help though, nonetheless :)Centner
I totally agree. I came up with a better alternative, but nonetheless it is dirty coding right now. ;-) I re-added the built in animation property from Bootstrap data-dismiss="modal" and just fading out the .modal-backdrop element which encapsules both the dialog and background. See updated answer!Chauncey
Many thanks again, but I still wasn't happy as this won't trigger any of the event handlers that Bootstrap modal uses, so it's not future-proof. I've gone for a solution that totally resolves all the issues I can think of and have posted it as an answer. Thanks for your help, and definitely +1 :)Centner
C
3

Okay, I don't like to answer my own question, but I've got a solution that is 100% foolproof (as far as I can tell). I've ended up going for a solution that checks for an existing dialog and modifies that, rather than hiding it and showing a new one.

Here's a working jsfiddle (using echo in the ajax call where it normally loads a html template)...

http://jsfiddle.net/ArchersFiddle/0302gLav/9/

The code is part of a larger library I'm working on, but I'll post it here anyway as it may well prove useful to others.

JavaScript Library

(function () {

    var _defaultOptions = {
        backdrop: "static",
        close: true,
        keyboard: true
    };

    window.bootstrap = {
        modal: {
            show: function (options) {

                options = $.extend(_defaultOptions, options);

                var buttonText = "";

                for (var key in options.buttons) {

                    options.buttons[key].id = "button_" + key.split(" ").join("");

                    var button = options.buttons[key];

                    buttonText += "<button type=\"button\" " +
                        "id=\"" + button.id + "\" " +
                        "class=\"btn " +
                        (typeof (button.class) == "undefined" ? "btn-default" : button.class) + "\" " +
                        (typeof (button.dismiss) == "undefined" ? "" : "data-dismiss=\"modal\" ") + ">" +
                        key + "</button>";
                }

                $.ajax({
                    url: "templates/bootstrap-modal.html"
                })
                .done(function (data) {
                    data = data
                        .replace("{:Title}", options.title)
                        .replace("{:Body}", options.body)
                        .replace("{:Buttons}", buttonText);

                    var $modal = $("#bootstrap-modal");
                    var existing = $modal.length;

                    if (existing) {
                        $modal.html($(data).find(".modal-dialog"));
                    }
                    else {
                        $modal = $(data).appendTo("body");
                    }

                    for (var key in options.buttons) {
                        var button = options.buttons[key];
                        if (typeof (button.callback) !== undefined) {
                            $("#" + button.id).on("click", button.callback);
                        }
                    }

                    $modal.off("shown.bs.modal").on("shown.bs.modal", function(e) {
                        if (typeof(options.onshow) === "function") {
                            options.onshow(e);
                        }
                    });

                    if (!existing) {
                        $modal.modal(options);
                    }

                    if (options.large === true) {
                        $modal.find(".modal-dialog").addClass("modal-lg");
                    }

                    if (options.close === false) {
                        $modal.find("button.close").remove();
                    }
                });
            },
            hide: function (callback) {
                var $modal = $("#bootstrap-modal");

                if (!$modal.length) return;

                $modal.on("hidden.bs.modal", function(e) {
                    $modal.remove();
                    if (typeof(callback) === "function") {
                        callback(e);
                    }
                });

                $modal.modal("hide");
            }
        }
    };
})();

Template HTML

<div id="bootstrap-modal" class="modal fade">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
        <h4 class="modal-title">{:Title}</h4>
      </div>
      <div class="modal-body">{:Body}</div>
      <div class="modal-footer">
        {:Buttons}
      </div>
    </div>
  </div>
</div>

Example usage:

bootstrap.modal.show({
    title: "Dialog Title",
    body: "<p>This is the dialog body and can contain any old html rubbish.</p>",
    buttons: {
        Close: {
            dismiss: true
        }
    }
});

Further options explained

bootstrap.modal.show({
    backdrop: true,                     // show the backdrop
    close: true,                        // show the close button
    keyboard: true,                     // allow the keyboard to close the dialog
    title: "Dialog Title",
    body: "<p>This is the dialog body and can contain any old html rubbish.</p>",
    buttons: {
        Button1: {
            class: "btn-primary",           // any class you want to add to the button
            dismiss: false,                 // does this button close the dialog?
            callback: function() {          // button click handler
                // the button was clicked - do something here
            }
        },
        Button2: {
            // options as defined as above.  You can have as many buttons as you want
        }
    },
    onshow: function(e) {
        // this will trigger when the dialog has completed the show animation
    }
});

and

bootstrap.modal.hide(function(e) {
    // this will trigger when the dialog has completed the hide animation
});

All the options in the show() method are optional, but obviously you'll want a title and a body.

Centner answered 7/8, 2014 at 14:18 Comment(0)
B
1

I've code how to close an opened modal before opening another one.

$('[data-toggle="modal"]').on('click', function() { //On click a button which call a modal
    if(($(".modal").data('bs.modal') || {}).isShown){ //If a modal is shown
        var modal = $(".modal.in"); // Get the current element
        $(modal).modal('hide'); // Hide the current modal
    }
});

Hope that helped!

Beckmann answered 21/5, 2015 at 21:8 Comment(1)
Thanks, but no. If you read the question again you'll see that I wanted to get rid of the animation between closing one dialog and opening another. Modifying the existing dialog was the most precise way to go.Centner
P
0

A bit late but might help somebody with the same problem.

Removing the fade class when showing the new modal is showing the backdrop without a fade class either.

For a clean transition, hiding the current modal and showing the new one without fade, but with fade out on closing:

// hide the existing modal, but without fade animation
$("#existing-modal").removeClass("fade").modal("hide");

// show the new modal without fade animation, but enable the fade animation for later
$("#new-modal").removeClass("fade").modal("show").addClass("fade");

// enable fade animation of the backdrop, that was removed for the new modal
$(".modal-backdrop.in").addClass("fade");

(with the most recent Bootstrap versions, use $(".modal-backdrop.show").addClass("fade")),

The example with the fix: http://jsfiddle.net/bvalentino/f82z1wex/2/

Plowman answered 10/8, 2020 at 15:20 Comment(0)
P
0

this code worked for me had old version Special thank to Antony GUEDJ use his code

$('[data-toggle="modal"]').on('click', function() {
   if($('.modal').hasClass('show')){
     var modal = $('.modal.show')
     $(modal).modal('hide');
    }
});
Petrolatum answered 16/2 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.