javascript中alert函数的替代方案,一个自定义的对话框的方法

  大家好,我们平时在使用Javascript的时候,经常会需要给用户提供一些反馈信息,完成这个功能有很多种方法。但在平时开发中我们用的最多的可能就是alert这个函数了(这里只说一般情况,不排除个别高手~),使用这个函数确实很方便,可以很简单的向用户提供一些交互信息。不过它也有很多不足,比如他的外观很简

单,而且不可控制,再有它属于浏览器级别的函数,是由各个浏览器自己实现的,所以在不同的浏览器上面,它的界面都不太一样。如果是在以前,这种情况或许很容

易被大多数用户所接受,不过随着时间的推移,用户越来越希望得到更好的体验。所以现在大家经常会在很多网站上看到JS做出来的各种对话框,这些界面会是用户体

验提升不少,所以今天就说说关于这方面的内容吧,进入正题,不说废话啦~

 

   首先给大家看看效果,先有一个直观的了解:

 

 

  如上图所示,经过我的测试,这个对话框可以使用在IE6 7 8,Firefox,Chrome等多个主流浏览器中。下面就和大家一起看看他的代码吧。

 

  1. 首先,我们需要判断浏览器的类型,这里用了几个bool变量来代表不同的浏览器。

 

 

 1 var springweb_typeIsIE = false;
 2 var springweb_typeIsGecko = false;
 3 var springweb_typeIsWebkit = false;
 4 
 5 var springweb_typeIsIE6 = false;
 6 var springweb_typeIsIE7 = false;
 7 var springweb_typeIsIE8 = false;
 8 var springweb_typeIsFireFox = false;
 9 var springweb_typeIsChrome = false;
10 var springweb_typeIsSafari = false;
11 
12 var agent = window.navigator.userAgent;
13 
14 if (agent.indexOf("MSIE 6"!= -1) {
15     springweb_typeIsIE6 = true;
16     springweb_typeIsIE = true;
17 }
18 else if (agent.indexOf("MSIE 7"!= -1) {
19     springweb_typeIsIE7 = true;
20     springweb_typeIsIE = true;
21 }
22 else if (agent.indexOf("MSIE 8"!= -1) {
23     springweb_typeIsIE8 = true;
24     springweb_typeIsIE = true;
25 }
26 else if (agent.indexOf("Firefox"!= -1) {
27     springweb_typeIsFireFox = true;
28     springweb_typeIsGecko = true;
29 else if (agent.indexOf("Chrome"!= -1) {
30     springweb_typeIsChrome = true;
31     springweb_typeIsWebkit = true;
32 }
33 else if (agent.indexOf("Safari"!= -1) {
34     springweb_typeIsSafari = true;
35     springweb_typeIsWebkit = true;
36 }

 

 

  如上所示,这里通过检测agent头来判断不同的浏览器,这是一个比较简单的判断方法。

  下面开始构造我们的对话框,在构造对话框前,我们先要制造一种模态窗体的效果(就是当对话框弹出来的时候,用户不能操作页面上的其余内容),这里我们就用一个黑色的透明层来完成这样的效果。

 1 document.body.style.overflowY = "hidden";
 2 document.body.style.overflowX = "hidden";
 3 var divBackground = document.createElement("div");
 4 divBackground.style.position = "absolute";
 5 divBackground.style.left = "0px";
 6 divBackground.style.top = document.body.scrollTop +  "px";
 7 divBackground.style.width = "100%";
 8 divBackground.style.height = "100%";
 9 if (springweb_typeIsChrome || springweb_typeIsFireFox) {
10     divBackground.style.backgroundColor = "rgba(0,0,0,0.7)";
11 else {
12     divBackground.style.backgroundColor = "#000000";    
13     divBackground.style.filter = "alpha(opacity=70)";
14 }
15 divBackground.style.zIndex = "99";
16 document.body.appendChild(divBackground);

 

   上面的代码,我们首先将浏览器的滚动条禁止,以防止用户在弹出对话框的时候滚动浏览器窗口,接下来设定相应的样式,一个比较重要的就是8-13行,这里根据浏览器的类型来应用不同的CSS样式来达到透明的效果,对于IE浏览器,我们使用IE自带的滤镜功能,而对于其他浏览器,我们使用基于CSS3的rgba方式实现透明效果。

  接下来,我们要构造对话框,这里首先创建了一个div层,来代表我们整个对话框。方法如下:

 

 

 

 1 var dialogWidth = 260;
 2 var dialogHeight = 120;
 3 var fontSize = 14;
 4 var lineWidth = document.body.clientWidth * 0.7;
 5 if ((fontSize * msg.length) < lineWidth) {
 6       dialogWidth = parseInt(fontSize * msg.length) + 50;
 7 else {
 8       dialogWidth = parseInt(lineWidth);
 9       dialogHeight += parseInt(((fontSize * msg.length) / lineWidth) * fontSize);
10             
11 }
12         
13 divDialog.style.width = dialogWidth + "px";
14 divDialog.style.height = dialogHeight + "px";        
15 divDialog.style.position = "absolute";
16 divDialog.style.border = "1px solid #C0D7FA";
17 divDialog.style.borderRight = "2px outset #DEDEDE";
18 divDialog.style.borderLeft = "2px outset #DEDEDE";
19 divDialog.style.left = ((document.body.clientWidth / 2- (dialogWidth / 2)) + "px";
20 divDialog.style.top = (document.body.scrollTop + (document.body.clientHeight / 2- (dialogHeight / 2)) + "px";
21 divDialog.style.zIndex = "100";

 

  

   这里,首先根据消息的字数计算了对话框的尺寸(这里的计算方法不一定最好,如果有更好的计算方法还望大家指教),后面那些DOM代码就不用我多解释了吧。 

 

  接下来,我们创建对话框的标题栏,这个用来显示对话框的标题,并且用它了实现对话框的拖动操作。

 

 

 1 var divHead = document.createElement("div");
 2 if (title != undefined) {
 3     divHead.innerHTML = title;
 4 else {
 5     divHead.appendChild(document.createTextNode("消息"));
 6 }
 7 divHead.style.width = "100%";
 8 divHead.style.height = "25px";
 9 divHead.style.lineHeight = "25px";
10 divHead.style.fontSize = "14px";        
11 divHead.style.fontWeight = "bold";
12 divHead.style.borderBottom = "1px outset #8989FF";
13 divHead.style.color = "white";
14 divHead.style.textIndent = "10px";
15 divHead.style.backgroundColor = "blue";
16 divHead.style.backgroundImage = "url('" + springweb_basePath  + "/images/headbg.png')";
17 divHead.style.cursor = "move";
18 divHead.onmousedown = function() {
19 
20     divDialog.dragging = true;
21     
22 };
23 divHead.onmouseup = function() {
24 
25     divDialog.dragging = false;
26 
27 };
28 
29 document.body.onmousemove = function(e) {
30 
31     if (!divDialog.dragging) return;
32     e = e || window.event;
33     var mouseX, mouseY;
34     var mouseOffsetX, mouseOffsetY;
35     if (e.pageX || e.pageY) {
36         mouseX = e.pageX;
37         mouseY = e.pageY;
38 
39     } else {
40         mouseX =
41             e.clientX + document.body.scrollLeft -
42             document.body.clientLeft;
43         mouseY =
44             e.clientY + document.body.scrollTop -
45             document.body.clientTop;
46 
47     }
48     
49     divDialog.style.left = (mouseX - dialogWidth * 0.4+ "px";
50     divDialog.style.top = (mouseY - 10+ "px";            
51 };
52         
53 
54 divDialog.appendChild(divHead);

 

 

  这里呢,有必要说一下的就是,鼠标按下和弹起事件,这里给div对象多增加了一个dragging的属性,用来代表对话框是否正在拖动中(这也是JS的特性之一,对object类型的对象指定新属性的一个方法:如果之前对象没有这个属性,只需通过对象名.属性名="值" 的方式,就可以为对象增加新属性),在鼠标移动事件中,我们通过对象的dragging属性,来决定是否移动对话框,关于对话框的移动位置,这里我偷懒了~没有判断对话框和鼠标的相对位置,而是给了一个常量,这样每次开始拖动时,对话框会稍微"瞬移"一下,有兴趣的朋友可以帮忙完善一下,呵呵。

 

   最后,是关于对话框内容区域的创建:

 

 

 1 var divContent = document.createElement("div");
 2 divContent.style.textAlign = "center";
 3 divContent.style.padding = "15px";
 4 divContent.style.fontSize = "12px";
 5 
 6 if (springweb_typeIsIE) {
 7     divContent.style.height = parseInt(dialogHeight - 25+ "px";
 8 else {
 9     divContent.style.height = parseInt(dialogHeight - 55+ "px";
10 }
11 
12 divContent.style.backgroundColor = "#ffffff";
13 if (springweb_typeIsIE6 || springweb_typeIsIE7 || springweb_typeIsIE8) {
14     divContent.style.filter =
15     "progid:DXImageTransform.Microsoft.Gradient(gradientType=1,startColorStr=#FFFFFF,endColorStr=#C2E2F8)";
16 else if (springweb_typeIsFireFox) {
17     divContent.style.backgroundImage =
18     "-moz-linear-gradient(left,rgba(255,255,255,1),rgba(194,226,248,1))";
19 else if (springweb_typeIsWebkit) {
20     divContent.style.backgroundImage =
21     "-webkit-gardient(linear,0% 0%,100% 100%,from(#FFFFFF),to(#000000))";
22 }
23 
24 
25 
26 
27 divContent.innerHTML = msg + "<br /><br />";
28 
29 
30 divDialog.appendChild(divContent);
31 
32 var closeButton = document.createElement("img");
33 closeButton.style.cursor = "hand";
34 closeButton.setAttribute("src", springweb_basePath + "/images/okButton.png");
35 closeButton.setAttribute("alt""确定");
36 
37 //the click event when the dialog is closing.
38 closeButton.onclick = function() {
39 
40     document.body.removeChild(divBackground);
41     document.body.removeChild(divDialog);
42     document.body.style.overflowY = "";
43     document.body.style.overflowX = "";
44 };
45 divContent.appendChild(closeButton);
46 divDialog.focus();
47 document.body.appendChild(divDialog);    

 

 

  这里应该不用多做解释了,稍微说一下,就是在13-22行的代码,这个是根据不同的浏览器来分别实现渐变效果,IE的话,用微软提供的渐变,Webkit或者Gecko内核的浏览器使用相应的CSS3标准也可以实现渐变效果。

 

  最后嘛,这个方法支持大多数浏览器,个别浏览器如果不能完全支持,还请各位见谅,期待大家有更加完善的方法一起讨论,下面是一个示例,有兴趣的朋友可以看看。转载请注明出处~

 

示例文件:示例文件下载

 

标签: Javascript, js, ui
posted @ 2010-03-21 21:36 Springfield 阅读(4347) 评论(35) 编辑 收藏

 回复 引用 查看   
#1楼 2010-03-21 22:21 heaiping      
你这个东西本身没有什么实际意义,但是这个过程还是很好的
 回复 引用 查看   
#2楼 2010-03-21 22:37 DeeRoad      
不错!
正好学习下,思路很清晰。
楼主继续。

 回复 引用 查看   
#3楼 2010-03-21 22:49 alahfun      
个人觉得有点太麻烦了,不过这种研究的精神是值得肯定,值得学习的。顶楼主
 回复 引用 查看   
#4楼 2010-03-22 08:27 zhangle      
这么简单的一个功能搞这么多代码
 回复 引用 查看   
#5楼 2010-03-22 08:30 clound      
你找一个基于jquery的弹出框插件改写下不是很方便,看到你还要自己判断浏览器类型,何必那么辛苦。
 回复 引用 查看   
#6楼 2010-03-22 08:41 lhking      
顶下楼主的探索发现精神
 回复 引用 查看   
#7楼[楼主] 2010-03-22 08:53 Springfield      
呵呵,很多东西拿过来就用,当然很容易,jquery我也很熟,我知道用那个只需要很少的代码。
但是,为什么像是jquery甚至.net framework亦或是JVM,这种基础的东西都是老外写的呢。拿来就用当然容易,但这两者的价值是不一样的,就像是一个给人做小网站的和一个写Java API的人相比,虽说都有其存在的必然价值,但你说哪个更有价值?
也许有人说重复发明轮子没有意义,但你在使用一个工具的时候,至少要尽量明白它的原理,且不说别的,这样你至少能把它用好。
当然,我也不是什么牛人,和那些高手也没有可比性,只是一种想法罢了,如果有人不赞同,那我以后尽量不在这发这种东西了 呵呵

 回复 引用 查看   
#8楼 2010-03-22 09:05 在云端      
@Springfield
楼主这样的回复大可不去理会。文章我还没看,先收藏了。推荐一个给你看看,也很强大。http://trentrichardson.com/Impromptu/index.php

 回复 引用 查看   
#9楼 2010-03-22 09:08 在云端      
但在平时开发中午我们用的最多的可能就是alert这个函数了
~~搜狗多带了个“午”字

 回复 引用 查看   
#10楼[楼主] 2010-03-22 09:15 Springfield      
@在云端
这个比我的强大多啦,呵呵,这个网站还是刚知道,谢谢分享啦,好好研究一下。

 回复 引用 查看   
#11楼[楼主] 2010-03-22 09:17 Springfield      
@在云端
呵呵 改过来了 谢谢指正

 回复 引用 查看   
#12楼 2010-03-22 09:28 Julin Rain      
建议楼主在button上面加上<div style="height:2000px;width:auto;"></div>
试试
<html>
    <head>
        <meta http-equiv="content-type" content="type=text/html;charset=gb2312" />
        <script src="spring/springweb.js" type="text/javascript" ></script>
        <script type="text/javascript" >                                       
            setBasePath("spring");                        
        </script>
        <script type="text/javascript" >

            function showDialog() {

                richAlert("您运气不错呀,这是我们的最新产品,欢迎体验~", "增强的消息框");

            }
            
        </script>
    </head>    
    <body >
    <div style="height:2000px;width:auto;"></div>        <button type="button" onclick="showDialog()" >Show</button>        
    </body>
</html>

 回复 引用 查看   
#13楼 2010-03-22 09:49 kuku_zhang      
楼主写的很好啊,如果能再次把它封装成服务端的控件,那么实用性会更高
 回复 引用 查看   
#14楼 2010-03-22 09:58 aXinNo1      
研究精神是可以称赞的,加油!
 回复 引用 查看   
#15楼[楼主] 2010-03-22 10:06 Springfield      
@Julin Rain
谢谢Rain的指正,确实这个问题考虑欠佳,现在已经修正过来了,还希望大家一起把它更加完善起来~

 回复 引用 查看   
#16楼 2010-03-22 10:28 New.min      

引用Springfield:
呵呵,很多东西拿过来就用,当然很容易,jquery我也很熟,我知道用那个只需要很少的代码。
但是,为什么像是jquery甚至.net framework亦或是JVM,这种基础的东西都是老外写的呢。拿来就用当然容易,但这两者的价值是不一样的,就像是一个给人做小网站的和一个写Java API的人相比,虽说都有其存在的必然价值,但你说哪个更有价值?
也许有人说重复发明轮子没有意义,但你在使用一个工具的时候,至少要尽量明白它的原理,且不说别的,这样你至少能把它用好。
当然,我也不是什么牛人,和那些高手也没有可比性,只是一种想法罢了,如果有人不赞同,那我以后尽量不在这发这种东西了 呵呵

有同感啊,支持

 回复 引用 查看   
#17楼 2010-03-22 10:33 Julin Rain      
@Springfield
很高兴你的及时回复,我不是来找茬的,只是希望我遇到的问题别人可以尽早发现:)

ps:alert效果是锁定屏蔽层下面的内容,而激活alert中的button的,所以再提两个建议:
1、当显示屏蔽层后,激活最上层alert中的按钮的focus事件;
2、对dom的max zindex进行缓存,并不是每个页面在加入这个alert之前maxzindex都是从0开始的,有些应用为了保持自己排在最上层,设置zindex=9999时有发生,所以如果整个document的js应用都是自己写的的话,建议对zindex统一分配。当然,这个问题已经超出alert应用的范围了;
3、要考虑到一些具有穿透力的dom的影响,比如select,flash/object,canvas等元素。

如果你对mootools也感兴趣,可以加我QQ(baicaiATcnyu.net),或许可以相互分享一些应用。

 回复 引用 查看   
#18楼 2010-03-22 10:35 Capricornus      
会停止加载吗???? Alert框 弹出后 后面是不准点的.. 不知道楼主所写的可不可以做到这样
 回复 引用 查看   
#19楼 2010-03-22 10:37 zengxiangzhan      
精神可嘉!支持!
 回复 引用 查看   
#20楼 2010-03-22 10:45 niky      
不错,学习基础的东西永远是真理!
 回复 引用 查看   
#21楼 2010-03-22 10:51 Dreampuf      
如果仅仅是因为调试...
console.debug(object);

 回复 引用 查看   
#22楼[楼主] 2010-03-22 12:28 Springfield      
@Julin Rain
谢谢你提出这么多的建议,这些问题都是我考虑的不周到的地方,关于zIndex这个,我想是不是可以遍历DOM找出当前网页中的最大值,然后再这个最大值的基础上加1呢?我这就加你QQ,呵呵

 回复 引用 查看   
#23楼[楼主] 2010-03-22 12:36 Springfield      
@Capricornus
这里采用了一个透明DIV的方法来让后面的内容不可点击,不过这个可能还会存在一些问题,但基本上还可以接受,如果有更好的方法还希望一起研究哈

 回复 引用 查看   
#24楼 2010-03-22 13:32 Gray Zhang      
不屏蔽TAB键的话,完全可以使用键盘去访问遮罩层之下的大部分元素
 回复 引用 查看   
#25楼 2010-03-22 13:40 左手牵右手      
人无聊啊,什么事情都可以去做。
哈哈。楼主你太有专研精神啦!
我无聊的就只会玩魔兽。哎!!!!

 回复 引用 查看   
#26楼 2010-03-22 14:34 Jason Deign      
@Springfield
林子大了什么鸟都有,别理那些无谓者,咱义无反顾的前进!

 回复 引用 查看   
#27楼 2010-03-22 16:19 Jimixu      
期待confirm框的替换版本,alert的div模拟框实在太普遍了
 回复 引用 查看   
#28楼 2010-03-22 16:26 Gray Zhang      
@Jimixu
confirm的返回值可就做不出来了,只能用回调函数,反而不舒服

 回复 引用 查看   
#29楼 2010-03-22 17:10 小haha      
lz,你写的全局变量一大堆,而且代码太多,根本用不了这么多代码,还有啊,设计的太粗糙了,细细,提一点意见,表拍我
 回复 引用 查看   
#30楼 2010-03-22 19:29 Ray Wu      
只能学习,无实用价值,太复杂了。为这么一个功能而加载这么多JS,不划算
 回复 引用 查看   
#31楼 2010-03-22 19:39 Capricornus      
嗯. 后层是不能点击的 但是按tab 能不能获得焦点??? 呵呵 有点挑刺了.. 不过大部分情况是可以使用这样的. 以前我们也做过一个弹出层, 还要选择<确定>和<取消>. 楼主可以试着把样式和js分开. 可以建个html用来视图化弹出层样式. 然后js 弹出的不是div 而是html 用模式窗口的话 是可以实现 这个页面不关闭,下面页面不可点击的.
 回复 引用 查看   
#32楼 2010-03-24 09:55 Julin Rain      
引用Jimixu:期待confirm框的替换版本,alert的div模拟框实在太普遍了

也很好模拟的

 回复 引用 查看   
#33楼 2010-03-24 09:57 Jimixu      
@Julin Rain
关键是confirm必须在操作确定取消以后返回一个值,而且期间必须中断js运行,这点比较麻烦

 回复 引用 查看   
#34楼 2010-03-24 10:07 Julin Rain      
难的是不好扩展,因为大家都口口声声在说,要让每个模块只负责一件事,但是往往我们都给它加入了太多的附加功能,就跟老板请人做事一样,之初都说得很明确,只需要做什么工作就OK,实际上都恨不能一个人当一千个人用。

alert的需求比较统一,那就是弹出一个窗口,显示一个提示,并且提示窗口后面的内容不可操作
comfrim的需求就千奇百怪,有人要返回一个bool值,有人要返回一个其他类型的字段,有些是一组,有些要给confirm的submit和cancel分别添加不同的事件,有些需要添加相同的事件,有些不需要添加事件,有些人想要让confirm提供alert,layer,iframe等其他类型的功能。

所以唯一能够满足这些所有需求的做法,就是把comfrim做成一个装饰器,显示标题,显示的内容,传递的参数,各个按钮的事件,这些交给client来决定。

 回复 引用 查看   
#35楼 2010-11-26 11:43 jiangjin89      
vb 相当漂亮的界面 ,好