Monday, January 12, 2009

Modal Dialogue - Based on Mootools and Clientcide script

Modal dialogue is one of the very commonly used dialogue on web these days. It is a special dialogue box that sits above all html elements on the screen. Nowadays the regular DHTML dialogue is combined with modal overlay - overlay that fills the screen with very high z-index and does not allow access to underlying UI elements. It is very cool to see a window turn light gray and a dialogue appearing center to the screen above this overlay. It is the current trend.

There are numerous script available to accomplish this. Clientcide is one among those.

Many times I wanted to center a html fragment- usually a div appear as overlay centered to the browser window, stay in center if they scroll the browser and stay in center if they resize the window as well and be a modal window...

I wanted to club both of the above feature ... basically a centered modal dialogue box above a grayed out background.

So, let us say, we have a div that hold some UI element - may be just an image or could be bunch of input fields. You want to make that DIV appear in the middle of the screen as modal dialogue combined with grayed out overlay. Here's is the simple script that will help you accomplish that:

Dependencies:

I wrote a wrapper functions to achieve the above requirement.

Below is my source code:

//Helper class to center element in current window, show element etc
var WindowHelper = {
defaultOptions: {
onError: $empty, animate: true
},
defaultStyle: {
position: 'fixed', 'z-index': 5100
},
_minOf: function(x, y) { // Utility function to find min of 2 numbers
if (($type(x) != 'number') || ($type(y) != 'number')) {
return -1;
}
if (x > y) {
return y;
}
return x;
},
_maxOf: function(x, y) { // Utility function to find max of 2 numbers
if (($type(x) != 'number') || ($type(y) != 'number')) {
return -1;
}
if (x < y) {
return y;
}
return x;
},
centerWindow: function(element, options) {
if (element) {
var top = window.getSize().y / 2;
var left = window.getSize().x / 2;
var opts = $merge(this.defaultOptions, options);

element.setStyle("display", "block"); //Without this below line does not give correct value.
top = this._maxOf(top - (element.getSize().y / 2), 0);
left = this._maxOf(left - (element.getSize().x / 2), 0);

element.setStyle("top", top);
element.setStyle("left", left);
element.setStyles(this.defaultStyle);

if (Browser.Engine.trident4) element.setStyle('position', 'absolute');

return true;
}
},
showElement: function(element, options) {
element.setStyle("visibility", "hidden");
element.setStyle("opacity", 0);
element.setStyle("display", "block");

if (options && options.animate) {
var myTween =
new Fx.Morph(element,
{
duration: 1000
, onComplete: this._showComplete(element)
});
myTween.start({ 'opacity': [0, 1] });
} else {
element.setStyle("opacity", 1);
}
},
_showComplete: function(element) {
element.setStyle('display', 'block');
element.setStyle('visibility', 'visible');
},
_closeComplete: function(element, options) {
element.setStyle('display', 'none');
if ($type(options.onWindowClose) == "function") {
options.onWindowClose();
}
},
close: function(element, options) {
if (element) {
if (options && options.animate) {
var myTween =
new Fx.Morph(element,
{
duration: 300
, onComplete: this._closeComplete(element, options)
});
myTween.start({ 'opacity': [.5, 0] });
} else {
this._closeComplete(element, options);
}
}
}
};

var CTModalizer = {
modalInstance: this.modalInstance || new Modalizer(),
defaultOpts: {
opacity: '0.5'
, hideOnClick: false
, 'z-index': 5000
, onPreGrab: $empty
, animate: true
, onWindowClose: $empty
, updateOnResize: true
},
init: function(elementIdToGrab, options) {
var opts = $merge(this.defaultOpts, options);

$$('.close').each(function(el) {
el.removeEvents("click");
});

$$('.close').each(function(el) {

el.addEvent("click", function() {
this.closeGrabbedWindow(elementIdToGrab);
} .bind(this));
}, this);

var grabEl = $(elementIdToGrab);
if (opts.updateOnResize) {
window.addEvent("resize", this._resize(grabEl, opts));
}
},
_resize: function(grabEl, opts) {
WindowHelper.centerWindow(grabEl, opts);
},
grab: function(elementIdToGrab, options) {
var grabEl = $(elementIdToGrab);
this.init(elementIdToGrab, options);
if (WindowHelper.centerWindow(grabEl, options)) {
var opts = $merge(this.defaultOpts, options);
WindowHelper.showElement(grabEl, opts);
this.modalInstance.modalShow(opts);
if ($type(opts.OnPreGrab) == "function") {
opts.OnPreGrab();
}
}
},
closeGrabbedWindow: function(grabbedElementId) {
var el = $(grabbedElementId);
el.setStyle("z-index", "4100");
WindowHelper.close(el, this.modalInstance.modalOptions);
this.modalInstance.modalHide();
}
};



Usage:

<script type="text/javascript" language="javascript">
var options = {
modalStyle: { opacity: 0.2 }
, animate: true
, onWindowClose: this.CloseRemovePopup };

function showPopUp(grabElementId) {
CTModalizer.grab(
grabElementId
, options);

}

function CloseRemovePopup() {
alert('Window closed');
}
</script>


Sample HTML:

<div id="ModalPopUp1" class="PopupContainer" style="width: 560px; height: 150px;">
Sample text <div class="close"><u>Close</u></div>
</div>

<input type="button" value="Show pop up" onclick="javascript:{ showPopUp('ModalPopUp1'); }" />

Not the class="close" - I use this class to identify the element to which I associate the close event.

Feel free to modify the scripts in case you need to .. :)

3 comments:

anutron said...

Nice work, but why not just use the StickyWin.Modal class on Clientcide?

http://www.clientcide.com/wiki/cnet-libraries/07-ui/07-stickywin.modal

Praveen said...

Thanks Anutron. You are right. But, StickyWin as such had too much of dependency with the Clientcide script other libraries - whereas cliencide modalizer was just an extenstion of mootools. Moreover, I had some callback function which I could call before binding - this was something required for my current project.

Tharan said...

Good work. I tried to use your coding. when the page load first time the div element shown up then i click the show up button it shown in modal box. How can i hide the div element when the page is loading first time. Please let me know.