模态对话框 - 增强版






4.54/5 (32投票s)
在本文中,我们将尝试生成一个可通过XMLHTTP连接从外部URL加载数据的可拖动DHTML层。这是我之前一篇可拖动层文章的增强版,因此它解决了前一篇文章中不存在的额外问题。
引言
本文可以看作是我之前文章的后续:《使用XML HTTP建模可拖动层并向其中加载动态内容》。如果您还没有看过,请先阅读一下,以便更快地跟上进度。
在本文中,我们将尝试:
- 创建一个跨浏览器、可拖动的DHTML模态对话框。
- 使用HTTP-POST发送AJAX请求。
- 获取响应并在模态对话框内显示。
最棒的是,借助sardalya API,我们将在不到20行代码中完成所有这些工作。
请注意,本文源文件压缩包中包含的API是sardalya的简化版本。您可以访问sardalya网站获取其最新的完整版本。
在开始之前,您可能想先看看最终效果。
创建视图 (View)
我们的ModalDialog
的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
" 是一个放置在页面内容和ModalDialog
窗口之间的层,这样可以防止意外点击其他页面元素,同时通过淡化背景页面来强调ModalDialog
。
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;
}
我们还需要一个“Opacity.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)
}
借助这些CSS,我们的框在大多数浏览器中看起来会像一个模态对话框,但某些浏览器除外。
善待Opera
我听到您说:“为什么我总是要善待Opera?Opera什么时候才能善待我?!” 我完全理解您:)。但让我们再次善待Opera一次。
为了让我们的透明背景在Opera中生效,我们需要更多的CSS调整。
/* transparency support for Opera */
.modalOpera
{
background-image: url("maskBg.png") !important;
}
就是这样!Opera不支持CSS透明度,但它完全支持.png透明度,因此将透明遮罩作为背景可以使我们的ModalDialog
在Opera中同样出色地工作。
这里还有一个问题。我们需要选择性地应用此类当且仅当用户代理是Opera时。也就是说,其他浏览器如Mozilla不需要这种透明度技巧,如果我们的用户代理是其中之一,我们就不应该使用“modalOpera
”类。我们稍后会解决这个问题。
脚本
首先,我们需要在页面加载时准备好模态对话框。
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
是sardalya的DOMManager
对象的一个实用方法。它会从DOM结构中移除空的文本节点。
现在,让我们逐个看看其他方法。
function addExtensionsForOpera()
{
/* classes for opera */
var ModalBG=new CBObject("ModalBG").getObject();
if(typeof(window.opera)!="undefined")
{
ModalBG.className="modalOpera";
}
}
如您所见,我们仅当用户代理是Opera时才将“modalOpera
”类名附加到“ModalBG”。
请注意,我们不是通过嗅探用户代理(navigator.userAgent
)来判断,而是通过“对象检测”(window.opera
)。
浏览器喜欢通过发送伪造的用户代理字符串来欺骗脚本,因此对象检测才是正确的方法。尽管其细节是整篇文章的主题,但我可以这样说:浏览器嗅探是“90年代”的做法。经验法则:始终使用对象检测。
接下来是附加opacity CSS部分。
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 http://www.sarmal.com/Exceptions.aspx
* 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);
}
然后是高度调整。
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);
}
}
然后我们用一行代码创建模态对话框。
g_Modal=new ModalDialog("ModalBG","DialogWindow",
"DialogContent","DialogActionBtn");
"ModalBG
" 是透明背景的ID,"DialogWindow
" 是模态对话框容器的ID,"DialogContent
" 是调用ModalDialog
的show
方法时显示消息的地方,而"DialogActionBtn
" 是关闭按钮的ID。
最后,我们向文档附加一个双击事件,该事件将触发一个AJAX操作。
/* Bind an event listener to double-click event. */
EventHandler.addEventListener(document,"dblclick",document_dblclick);
现在让我们看看document_dblclick
方法。
function document_dblclick(evt)
{
/* create an AJAX request */
var ajax = _.ajax();
/*
* Note that _.ajax(); is a shorthand notation
* for new XHRequest();
* Visit http://sardalya.pbwiki.com/Shortcuts 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();
}
注释应该能自明。
最后,是服务器回发后触发的两个方法。
/* 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();
}
就是这样!
那些讨厌的SELECT框怎么办?
ModalDialog
对象内部会处理这个问题,当ModalDialog
打开时,它会用带有"modalWrap
"类的SPAN
元素替换它们。这解决了众所周知的“SELECT框会穿透我的顶层”的问题。
为了完整起见,这是它的CSS。
.modalWrap
{
border: 2px #ffffcc inset;
background:#ffffcc;
margin:5px;
}
您可以向其中添加任意多条规则。SPAN
越像SELECT
元素越好(您可以应用宽度和行高,设置display为inline-table...等,我没有过多更改以保持简单)。
对于那些想知道如何替换这些SPAN
和SELECT
的人,下面是相应的私有方法。您可以查看文章Zip文件的源代码以获取更多详细信息。
在前一个版本中,我们只是通过将SELECT
的CSS可见性设置为hidden
来隐藏它们。在实际场景中进行测试后,我发现SELECT
的“突然消失”让一些用户感到烦恼。
我认为,转换SELECT
元素比完全隐藏它们要好得多。
……不,我不想使用IFRAME
s:)
代码如下:
_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();
}
}
};
结论
总之,我们建模并创建了一个可拖动的DHTML模态对话框,并建立了一个到外部脚本的AJAX连接;我们进行了一些跨浏览器调整,以使我们的应用程序在尽可能多的浏览器上运行;并且我们进行了一些面向对象的编码。
一如既往,祝您编码愉快!
历史
- 2006-06-02:文章创建。