Click here to Skip to main content
15,886,137 members
Articles / Web Development / CSS

Modal Dialog - Enhanced

Rate me:
Please Sign up or sign in to vote.
4.54/5 (34 votes)
8 Jun 2006CPOL4 min read 236.3K   1.6K   89   53
In this article, we will try to generate a draggable DHTML layer that loads data from an external URL via an XMLHTTP connection. This is an enhanced version of my previous Draggable Layer article, hence it addresses additional issues that are not present in the former article.

Sample Image - ModalDialogV2.gif

Introduction

This article can be considered as a follow up to my former article: Modeling a Draggable Layer and Loading Dynamic Content to it via XML HTTP. Have a look at it, if you have not done already, so that you can catch up faster.

In this article, we will try to:

  • Create a cross-browser, draggable DHTML modal dialog.
  • Send an AJAX request using HTTP-POST.
  • Get the response and display it inside the modal dialog.

And best of all, we will do all these in less than 20 lines of code, with the help of the sardalya API.

Please note that the API enclosed in this article's source archive is a rather simplified version of sardalya. You can visit sardalya's website for its latest full version.

Before starting, you may want to see the final result in action first.

Creating the View

The HTML of our ModalDialog is fairly simple:

HTML
<div id="ModalBG"></div>
 
<div id="DialogWindow">
  <div id="DialogHeader">
    <span id="DialogTitle">Title comes here</span>
    <img id="DialogActionBtn" src="icn_close.png" 
          alt="close icon" title="" />
  </div>
  <div id="DialogIcon"><img id="DialogIcon" 
     src="icn_alert.png" alt="alert icon" title="" /></div>
  <div id="DialogContent">...</div>
</div>

"ModalBG" is a layer that is placed between the page content and the ModalDialog window so that we prevent accidental clicks on other page elements, and at the same time, put some visual emphasis on the ModalDialog by fading the page in the background.

The CSS

To make our "dialog window" layer resemble an actual modal dialog, we need some CSS tweaks:

Master.css
CSS
#DialogWindow
{
 border: 1px #FFFFFF outset;
 width: 550px;
 display: none;
 background: #FFFFFF;
 z-index: 1000;
 position: absolute;
 top: 0;
 left: 0;
}
 
#DialogContent
{
 float: right;
 margin-right: 10px;
 margin-bottom: 10px;
 margin-top: 24px;
 width: 450px;
 display: block;
 font-size: 90%;
}
 
#DialogIcon
{
 padding: 10px;
 float: left;
}
 
#DialogHeader
{
 border-bottom: 1px #00449E outset;
 background: #00449E;
 text-align: right;
}
 
#DialogTitle
{
 float: left;
 padding: 8px;
 color: #FFFFFF;
}
 
#DialogActionBtn
{
 cursor:pointer;
}

And we need an "Opacity.css" for transparency support:

Opacity.css
CSS
#ModalBG
{
 width:100%;
 display:none;
 background-color:#333333;
 position:absolute;
 top:0;
 left:0;
 height:100%;
 z-index:999;
 opacity:.40;
 filter:alpha(opacity=40)
}

With the help of this CSS, our boxes will pretty much look like a modal dialog, except for certain browsers:

Be Kind to Opera

I hear you say, "Why am I to be kind to Opera all the time? Why is Opera never kind to me?!" and I truly understand you :). But let us be kind to Opera once again:

To make our transparent background work on Opera, we need several more CSS tweaks:

Master.css
CSS
/* transparency support for Opera */
.modalOpera
{
 background-image: url("maskBg.png") !important;
}

That's it! Opera does not understand CSS transparency, but it fully supports .png transparency, therefore a transparent mask as a background will make our ModalDialog work equally good in Opera.

There is one remaining issue here. We need to selectively apply this class if and only if the user agent is Opera. That is, other browsers such as Mozilla does not require this transparency hack, and we should not use the "modalOpera" class if our user agent is one of them. We will address this issue in a second.

The Script

First of all, we need to prepare the modal dialog at page load:

JavaScript
window.onload=function()
{
  /* Sweep unnecessary empty text nodes. */
  DOMManager.sweep();

  /*
   * Attach supporting css bind required
   * classes for transparency support in Opera.
   */ 
  addExtensionsForOpera();
 
  /* Attach opacity css. */
  attachOpacityCSS();
 
  /* Adjust height. */
  adjustHeight();

  /* Create the modal dialog */
  g_Modal=new ModalDialog("ModalBG","DialogWindow",
   "DialogContent","DialogActionBtn");
 

  /* Bind an event listener to double-click event. */
  EventHandler.addEventListener(document,"dblclick",document_dblclick);
  
  /* Re-adjust height on window resize */
  EventHandler.addEventListener(window,"resize",window_resize);
};

sweep is a utility method of sardalya's DOMManager object. It removes empty text nodes from the DOM structure.

Now, let us look at other methods one by one:

JavaScript
function addExtensionsForOpera()
{
  /* classes for opera */
  var ModalBG=new CBObject("ModalBG").getObject();
  if(typeof(window.opera)!="undefined")
  {
   ModalBG.className="modalOpera";
  }
}

As seen, we only append the "modalOpera" class name to "ModalBG" if the user agent is Opera.

Note that we do not sniff the user agent (navigator.userAgent) but do an "object detection" instead (window.opera).

Browsers love to fool scripts by sending false user agent strings and therefore object detection is the way to go. Although details of it is the subject of an entire article, I can say that browser sniffing is so '90s. As a rule of thumb, always use object detection.

Then comes attaching the opacity CSS piece:

JavaScript
function attachOpacityCSS()
{
  /*
   * CSS for opacity support
   * Note that this can be directly added to the body.
   * If you do not care about blindly adhering to standards
   * you can directly include the rules into Master.css
   *
   * Do I care? Yes and No.(visit <a href="%22http://www.sarmal.com/Exceptions.aspx%22">http://www.sarmal.com/Exceptions.aspx</a>
   * to learn how I feel about it).
   */
    var opacityCSS = document.createElement("link");
    opacityCSS.type="text/css";
    opacityCSS.rel="stylesheet";
    opacityCSS.href="Opacity.css";
    document.getElementsByTagName("head")[0].appendChild(opacityCSS);
}

And the height adjustment:

JavaScript
function adjustHeight()
{
  /* get the available height of the viewport */
  var intWindowHeight=WindowObject.getInnerDimension().getY();
  var dynModalBG=new DynamicLayer("ModalBG");
  var intModalHeight=dynModalBG.getHeight();
 
  /*
   * if modal background's height is less than the viewport's 
   * available height, increase its height.
   */
  if(intModalHeight<intWindowHeight)
  {
   dynModalBG.setHeight(intWindowHeight);
  }
}

Then we create the modal dialog in just a single line:

JavaScript
g_Modal=new ModalDialog("ModalBG","DialogWindow",
   "DialogContent","DialogActionBtn");

"ModalBG" is the ID of the transparent background, "DialogWindow" is the ID of the modal dialog container, "DialogContent" is where messages are displayed when calling the show method of ModalDialog, and "DialogActionBtn" is the ID of the close button.

And finally, we attach a double-click event to the document which will trigger an AJAX action:

JavaScript
/* Bind an event listener to double-click event. */
EventHandler.addEventListener(document,"dblclick",document_dblclick);

Now let us have a look at the document_dblclick method:

JavaScript
function document_dblclick(evt)
{
  /* create an AJAX request */
  var ajax = _.ajax();

  /*
   * Note that _.ajax(); is a shorthand notation 
   * for new XHRequest();
   * Visit <a href="%22http://sardalya.pbwiki.com/Shortcuts%22">http://sardalya.pbwiki.com/Shortcuts</a> for details.
   */

  /* 
   * You can add as many fields as you like to the post data. 
   * Normally the server will use this data to create an
   * output that makes sense which may be an XML, a JSON String
   * or an HTML String.
   */
  ajax.removeAllFields();
  ajax.addField("name","John");
  ajax.addField("surname","Doe");

  /* These events will be fired when server posts back a response. */
  ajax.oncomplete=ajax_complete;
  ajax.onerror=ajax_error;

  /* Set a default waiting message. */
  g_Modal.show("Fetching data... Please wait...");

  /*
   * Disable close action if you want to force the user 
   * to wait for the outcome of the AJAX request.
   * Although it is generally not recommended 
   * this may be necessary at certain times.
   */
  g_Modal.disableClose();

  /* Post data to the server. */
  ajax.get("externalScript.html");

  /* Stop event propagation. */
  new EventObject(evt).cancelDefaultAction();
}

The comments should be self-explanatory.

And finally, the two methods that are triggered after the server's post back:

JavaScript
/* Triggered when a successful AJAX response comes from the server.*/
function ajax_complete(strResponseText,objResponseXML)
{
  g_Modal.show(strResponseText);
  
  /* Re-activate close button. */
  g_Modal.enableClose();
}

/* Triggered when server generates an error. */
function ajax_error(intStatus,strStatusText)
{
  g_Modal.show("Error code: ["+ intStatus+ "] error message: [" + 
   strStatusText + "].");

  /* Re-activate close button. */
  g_Modal.enableClose();
}

That's it!

What About those Nasty SELECTs ?

The ModalDialog object internally handles it, by replacing them with SPAN elements with the class "modalWrap" whenever ModalDialog opens. This sorts out the well known "SELECTs bleed through my top layer" issue.

Here is the CSS of it for the sake of completeness:

CSS
.modalWrap
{
 border: 2px #ffffcc inset;
 background:#ffffcc;
 margin:5px;
}

You can add as many rules as you like to it. The more the SPAN resembles a SELECT element, the better (you can apply width and line-height, set display to inline-table... etc., I did not change it too much to keep it simple).

For those who wonder how the replacement of those SPANs and SELECTs are done, the corresponding private method is given below. You can observe the source code of the article's Zip file for more details.

In the former version, we were simply hiding the SELECTs by setting their CSS visibility to hidden. Having tested it in real-life scenarios, I saw that the "all of a sudden" disappearance of SELECTs was annoying to some of the users.

In my opinion, transforming the SELECTs is much better than hiding them completely.

... And no, I do not want to use IFRAMEs :)

Here follows the code:

JavaScript
_this._replaceCombos=function(blnReplaceBack)
{
 var arSelect = document.getElementsByTagName("select");
 var len=arSelect.length;
 var objSel=null;
 var strNodeValue="";
 var objSpan=null;
 var o=null;

 if(!blnReplaceBack)
 {
  blnReplaceBack=false;
 }
 
 for(var i=0;i<len;i++)
 {
  objSel=arSelect[i];
  strNodeValue=objSel.childNodes[objSel.selectedIndex
  ].childNodes[0].nodeValue;

  objSpan=new CBObject(objSel.id+"_ModalWrap");
  if(objSpan.exists())
  {
   o=objSpan.getObject();
   o.parentNode.removeChild(o);
  }

  objSpan=document.createElement("span");
  objSpan.id=objSel.id+"_ModalWrap";
  objSpan.appendChild(document.createTextNode(strNodeValue));
  objSpan.className="modalWrap";
  objSel.parentNode.insertBefore(objSpan,objSel);

  if(blnReplaceBack)
  {
   new DynamicLayer(objSpan).collapse();
   new DynamicLayer(objSel).expandInline();
  }
  else
  {
   new DynamicLayer(objSpan).expandInline();
   new DynamicLayer(objSel).collapse();
  }
 }
};

Conclusion

In conclusion, we modeled and created a draggable DHTML modal dialog and established an AJAX connection to an external script; we did some cross-browser tweaks to make our application work on as many browsers as possible, and we did some OO coding.

And as always, happy coding!

History

  • 2006-06-02: Article created.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Turkey Turkey
Volkan is a java enterprise architect who left his full-time senior developer position to venture his ideas and dreams. He codes C# as a hobby, trying to combine the .Net concept with his Java and J2EE know-how. He also works as a freelance web application developer/designer.

Volkan is especially interested in database oriented content management systems, web design and development, web standards, usability and accessibility.

He was born on May '79. He has graduated from one of the most reputable universities of his country (i.e. Bogazici University) in 2003 as a Communication Engineer. He also has earned his Master of Business Administration degree from a second university in 2006.

Comments and Discussions

 
GeneralMessage Closed Pin
1-Sep-08 17:09
mayurmv1-Sep-08 17:09 
Questioncross browser? Pin
mikedepetris17-Oct-06 6:45
mikedepetris17-Oct-06 6:45 
AnswerRe: cross browser? Pin
volkan.ozcelik18-Oct-06 10:26
volkan.ozcelik18-Oct-06 10:26 
GeneralRe: cross browser? Pin
mikedepetris20-Oct-06 7:24
mikedepetris20-Oct-06 7:24 
Questionuse with java applets? Pin
rsd0605-Oct-06 10:24
rsd0605-Oct-06 10:24 
AnswerRe: use with java applets? Pin
volkan.ozcelik5-Oct-06 17:49
volkan.ozcelik5-Oct-06 17:49 
GeneralRe: use with java applets? Pin
chintancode15-Jan-07 22:06
chintancode15-Jan-07 22:06 
GeneralOpacity Pin
Dewey15-Jul-06 15:57
Dewey15-Jul-06 15:57 
This technique has been around for quite some time. I use an almost identical technique, however the window is not draggable, and XmlHttp is not used, but it's effective as a modal dialog.

The good thing is that it can be used for any purpose, while yours is heavily restricted.

Either way, good work.
GeneralRe: Opacity Pin
volkan.ozcelik15-Jul-06 20:23
volkan.ozcelik15-Jul-06 20:23 
GeneralNice Work Pin
Komil13-Jul-06 12:16
Komil13-Jul-06 12:16 
GeneralRe: Nice Work Pin
volkan.ozcelik14-Jul-06 21:44
volkan.ozcelik14-Jul-06 21:44 
GeneralMerhaba Pin
Ashley van Gerven13-Jul-06 0:11
Ashley van Gerven13-Jul-06 0:11 
GeneralRe: Merhaba Pin
volkan.ozcelik13-Jul-06 0:19
volkan.ozcelik13-Jul-06 0:19 
GeneralExcellent work Pin
Carl Reid15-Jun-06 4:06
Carl Reid15-Jun-06 4:06 
GeneralRe: Excellent work [modified] Pin
volkan.ozcelik15-Jun-06 5:05
volkan.ozcelik15-Jun-06 5:05 
GeneralRocking Pin
Dinuj Nath15-Jun-06 3:13
Dinuj Nath15-Jun-06 3:13 
GeneralRe: Rocking Pin
volkan.ozcelik15-Jun-06 4:41
volkan.ozcelik15-Jun-06 4:41 
GeneralSmall bug Pin
Gevorg8-Jun-06 10:14
Gevorg8-Jun-06 10:14 
AnswerRe: Small bug Pin
volkan.ozcelik8-Jun-06 20:51
volkan.ozcelik8-Jun-06 20:51 
AnswerRe: Small bug Pin
volkan.ozcelik8-Jun-06 20:51
volkan.ozcelik8-Jun-06 20:51 
GeneralVery cool stuff Pin
Stefan Scholte5-Jun-06 21:41
Stefan Scholte5-Jun-06 21:41 
GeneralRe: Very cool stuff Pin
volkan.ozcelik5-Jun-06 21:45
volkan.ozcelik5-Jun-06 21:45 
GeneralNice Pin
gia nghia3-Jun-06 17:05
gia nghia3-Jun-06 17:05 
GeneralRe: Nice Pin
volkan.ozcelik3-Jun-06 19:12
volkan.ozcelik3-Jun-06 19:12 
GeneralRe: Nice Pin
Yclkvnc4-Jun-06 20:39
Yclkvnc4-Jun-06 20:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.