Making objected oriented widgets with prototype and scriptaculous becomes easy with prototype’s Class.create function. In this demo, I will examine how to make a custom alert dialog.
First, let’s take a quick peek at the code:
/**
* @author Andy Daykin
* @website http://andydaykin.com
* @param {Object} text
* @param {Object} callerElem
*/
var Dialog = Class.create({
initialize: function (text, callerElem) {
var height = (document.height !== undefined) ? document.height : document.body.offsetHeight;
height += 25;
new Insertion.Bottom(document.body, "<div id='overlay' style='position: absolute; width: 100%; top: 0px; left: 0px; z-index: 100;'></div>");
$('overlay').setStyle({ height: height + "px" });
var div = document.createElement('div');
Element.extend(div);
div.id = 'dialog';
div.addClassName('dialog');
div.update("<div class='dialogWrap' id='dialog'><div id='dialogHandle'><span class='dialogHeader'>Error:</span><span id='dialogClose'>X</span><br />" +
"</div><div id='dialogDivider' style='display: none;'></div><p>" + text + "</p><div class='dialogBtn' id='okBtn'>OK</div></div>");
div.setStyle({ 'display': 'none' });
document.body.appendChild(div);
// Calculate position
var height = div.getHeight() / 2;
var width = div.getWidth() / 2;
var scroll = (document.all) ? document.body.scrollTop : window.pageYOffset;
// Calculate the middle of the screen
var top = (document.viewport.getHeight() + scroll) / 2 - height;
var left = document.viewport.getWidth() / 2 - width;
div.setStyle({ 'left': left + 'px', 'top': top + 'px' });
div.show();
$('dialogClose').observe('click', this.closeDialog.curry(callerElem));
$('okBtn').observe('click', this.closeDialog.curry(callerElem));
new Draggable('dialog', { handle: 'dialogHandle',
starteffect: function(e) {
$('dialogDivider').show();
$('dialogHandle').setStyle({ backgroundColor: '#5777AF' });
new Effect.Opacity('dialog', { from: 1.0, to: 0.7 });
},
endeffect: function(e) {
$('dialogDivider').hide();
$('dialogHandle').setStyle({ backgroundColor: '#2C508F' });
new Effect.Opacity('dialog', { from: 0.7, to: 1.0 });
}
});
},
closeDialog: function(callerElem) {
$('overlay').remove();
new Effect.Fade('dialog', { duration: .8, afterFinish: function(e) {
$('dialog').remove();
callerElem.focus();
}});
}
});
There is a reserved function in prototype classes called “initialize”, which acts as a constructor function, for those of you who have experience with other object oriented languages. In my initialize function, I take two parameters, the text and the caller element. The text is the message which will be displayed in the dialog. The text parameter can contain both a plain text string and HTML in it, since prototype’s update function can take in either. The callerElem is the id of the element which will take focus after the dialog is closed.
In order to make this a true alert statement, we need to display an overlay, so the user must click on the dialog before they can do anything else. To make our overlay we need to find the width and height of the user’s screen.
var height = (document.height !== undefined) ? document.height : document.body.offsetHeight;
height += 25;
new Insertion.Bottom(document.body, "<div id='overlay' style='position: absolute; width: 100%; top: 0px; left: 0px; z-index: 100;'></div>");
$('overlay').setStyle({ height: height + "px" });
After the height is obtained, we need to insert the overlay at the bottom, so it covers the entire document. Note the z-index value set at 100, so the overlay is set on top of everything.
Next we need to create our dialog widget. The dialog already has some css to make it more user friendly, but I will leave that part up to you, to style your own widget. It is important to note that the dialog css class does have some rules which must be put in place for it to function properly.
.dialog {
border: 1px solid black;
position: absolute;
width: 275px;
background-color: #2C508F;
color: white;
z-index: 200;
}
The z-index on your dialog should be greater than whatever you set your overlay to, so the dialog shows up.
Now let’s create the element and calculate or position. To do so, we will use document.createElement, and then extend the element. By extending the element you are able to use prototype’s element methods on the widget, such as setStyle, addClassName, and update.
After setting the styles we can calculate the middle of the screen so our dialog mimics the behavior of the alert dialog.
var div = document.createElement('div');
Element.extend(div);
div.id = 'dialog';
div.addClassName('dialog');
div.update("<div class='dialogWrap' id='dialog'><div id='dialogHandle'><span class='dialogHeader'>Error:</span><span id='dialogClose'>X</span><br />" +
"</div><div id='dialogDivider' style='display: none;'></div><p>" + text + "</p><div class='dialogBtn' id='okBtn'>OK</div></div>");
div.setStyle({ 'display': 'none' });
document.body.appendChild(div);
// Calculate position
var height = div.getHeight() / 2;
var width = div.getWidth() / 2;
var scroll = (document.all) ? document.body.scrollTop : window.pageYOffset;
// Calculate the middle of the screen
var top = (document.viewport.getHeight() + scroll) / 2 - height;
var left = document.viewport.getWidth() / 2 - width;
div.setStyle({ 'left': left + 'px', 'top': top + 'px' });
Once we have set our style and placed the element properly it’s time to show it and add our close event handler. By using prototype’s curry method we can pass along the id of our element to the close event handler. We also make our dialog draggable, and change the style of the upper part of our element when we do so to give the handle effect.
div.show();
$('dialogClose').observe('click', this.closeDialog.curry(callerElem));
$('okBtn').observe('click', this.closeDialog.curry(callerElem));
new Draggable('dialog', { handle: 'dialogHandle',
starteffect: function(e) {
$('dialogDivider').show();
$('dialogHandle').setStyle({ backgroundColor: '#5777AF' });
new Effect.Opacity('dialog', { from: 1.0, to: 0.7 });
},
endeffect: function(e) {
$('dialogDivider').hide();
$('dialogHandle').setStyle({ backgroundColor: '#2C508F' });
new Effect.Opacity('dialog', { from: 0.7, to: 1.0 });
}
});
Finally we come to our close handler. When the close handler is clicked, the overlay will be removed. After the overlay is removed, we get rid of the dialog, and focus on the element that initially triggered the dialog.
closeDialog: function(callerElem) {
$('overlay').remove();
new Effect.Fade('dialog', { duration: .8, afterFinish: function(e) {
$('dialog').remove();
callerElem.focus();
}});
}
Calling the dialog widget is just a simple matter of saying
var form = $('contact');
if($(form['name']).getValue() == "") {
new Dialog("Please enter your name", $(form['name']));
return false;
}