How to achieve a queue of object instances of jQuery modals, assuring only one instance is on per time?

146 Views Asked by At

I've a task of building a modal prompt, that's been simple so far describing its methods like "show", "hide" when it comes down just to DOM manupulation.

Now comes the hardship for me... Imagine we have a page on which there are several immediate calls to construct and show several modals on one page

//on page load:

$("browser-deprecated-modal").modal();
$("change-your-city-modal").modal();
$("promotion-modal").modal();

By default my Modal (and other libraries i tried) construct all of these modals at once and show them overlapping each other in reverse order - i.e $(promotion-modal) is on the top, while the $("browser-deprecated-modal") will be below all of them. that's not what i want, let alone overlapping overlays.

I need each modal to show up only when the previous one (if there'are any) has been closed. So, first we should see "browser-deprecated-modal" (no other modals underneath), upon closing it there must pop up the second one and so on.

I've been trying to work it out with this:

$.fn.modal = function(options) {

  return this.each(function() {              
    if (Modal.running) {
        Modal.toInstantiateLater.push({this,options});
    } else {
         var md = new Modal(this, options);
    }
});
}

destroy :function () {
   ....
  if (Modal.toInstantiateLater.length)
  new Modal (Modal.toInstantiateLater[0][0],Modal.toInstantiateLater[0][1]);
}

keeping a track of all calls to construct a Modal in a array and in the "destroy" method make a check of this array is not empty. but it seems awkward and buggy me thinks. i need a robust and clear solution. I've been thinking about $.Callbacks or $.Deferred, kinda set up a Callback queue

if (Modal.running) { //if one Modal is already running

   var cb = $.Callbacks();
   cb.add(function(){
      new Modal(this, options);
   });

} else { //the road is clear
  var md = new Modal(this, options);
} 

and to trigger firing cb in the destroy method, but i'm new to this stuff and stuck and cannot progress, whether it's right or not, or other approach is more suitable. Besides, I read that callbacks fire all the functions at once (if we had more than one extra modal in a queue), which is not right, because I need to fire Modal creation one by one and clear the Callback queue one by one.

Please help me in this mess.

My code jsfiddle

1

There are 1 best solutions below

8
On BEST ANSWER

I got rid of the counter variable, as you can use toInstantiateLater to keep track of where you are, and only had to make a few changes. Give this a try...

Javscript

function Modal(el, opts){        
    this.el = $(el);
    this.opts = opts;
    this.overlay = $("<div class='overlay' id='overlay"+Modal.counter+"'></div>");
    this.wrap = $("<div class='wrap' id='wrap"+Modal.counter+"'></div>");
    this.replace = $("<div class='replace' id='replace"+Modal.counter+"'></div>");
    this.close = $("<span class='close' id='close"+Modal.counter+"'></span>")

    if (Modal.running) {
        Modal.toInstantiateLater.push(this);
    }
    else {
        Modal.running = true;
        this.show();
    }
}

Modal.destroyAll = function() {
    Modal.prototype.destroyAll();
};

Modal.prototype = {

    show: function() {
        var s = this;
        s.wrap.append(s.close);
        s.el.before(s.replace).appendTo(s.wrap).show();
        $('body').append(s.overlay).append(s.wrap);
        s.bindEvents();
        Modal.currentModal = s;
    },


    bindEvents: function() {
        var s = this;

        s.close.on("click.modal",function(e){
            s.destroy.call(s,e);
        });
    },

    destroy: function(e) {
        var s = this;

        s.replace.replaceWith(s.el.hide());
        s.wrap.remove();
        s.overlay.remove();

        if (Modal.toInstantiateLater.length > 0) {
            Modal.toInstantiateLater.shift().show();
        }
        else {
            Modal.running = false;
        }
    },

    destroyAll: function(e) {
        Modal.toInstantiateLater = [];
        Modal.currentModal.destroy();
    }

}

Modal.running = false;
Modal.toInstantiateLater = [];
Modal.currentModal = {};

$.fn.modal = function(options) {

      return this.each(function() {              
        var md = new Modal(this, options);
    });
}

$("document").ready(function(){

    $("#browser-deprecated-modal").modal();
    $("#change-your-city-modal").modal();
    $("#promotion-modal").modal();

    $("#destroy-all").on("click", function() {
        Modal.destroyAll();
    });
});

jsfiddle example

http://jsfiddle.net/zz9ccbLn/4/