18.4 客户端基础代码
上节介绍了服务器端的一些底层运行的程序代码,与之相对应的是运行在客户端的一些基础程序代码。Web QQ的客户端基础程序代码主要分为4部分:封装XMLHTTP操作的tools.js文件,提供具有亲和力消息提示框的myAlert.js文件,显示QQ样式菜单的qq.js文件,简易的拖动类Drag,以及包含一个小型HTML编辑器的chat.js文件。下面将分别讲述这5部分代码的具体实现。
18.4.1 封装XMLHTTP
任何不基于Ajax框架的Ajax程序的开发,第一步都是要封装XMLHTTP对象,本示例程序也不例外。在本例中对XMLHTTP对象封装的代码全部包含在tools.js文件中。
在tools.js文件的开始首先定义了一个“$”方法,它用来替代“document.getElementById”方法。然后定义了一个Request对象,并为其定义了sendPOST和sendGET等方法。在文件的最后定义了一个常用方法closeme()用来关闭和删除窗口中的一个显示元素。本文件中的程序运行逻辑和前面章节中的xmlhttp.js文件的逻辑大致相同,读者参照前面的介绍应该不难理解tools.js文件。tools.js文件的具体代码如下:
//getElementById的进一步封装
function $() {
var elements = new Array(); //用于返回的数组元素
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if(typeof element == 'string') //得到元素
element = document.getElementById(element);
if (arguments.length == 1) //存在一个元素,直接返回这个元素
return element;
elements.push(element); //存在多个元素,添加入数组
}
return elements;
}
//封装XMLHTTP的Request类的代码
var Request = new Object();
//定义一个XMLHTTP的数组
Request.reqList = [];
//创建一个XMLHTTP对象,以便兼容不同的浏览器
function getAjax()
{
var ajax=false;
try //针对IE浏览器创建对象
{
ajax = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
Try //若未创建成功,则创建另一个
{
ajax = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (E)
{
ajax = false;
}
}
//非IE浏览器,则初始化对象
if (!ajax && typeof XMLHttpRequest!='undefined')
{
ajax = new XMLHttpRequest();
}
return ajax;
}
//封装XMLHTTP向服务器发送请求的操作
//url:向服务器请求的路径;method:请求的方法,即是GET还是POST;callback:当服务器成功返
//回结果时,调用的函数
//data:向服务器请求时附带的数据;callback2;当服务器返回错误时调用的函数
Request.send = function(url, method, callback, data, callback2) {
var req=getAjax(); //得到一个XMLHTTP的实例
//当XMLHTTP的请求状态发生改变时调用
req.onreadystatechange = function() {
if(req.readyState == 4) { // 当请求已经加载
if(req.status < 400) { // 当请求返回成功
if(callback) //当定义了成功回调函数时,执行成功回调函数
(method=="POST") ? callback(req,data) : callback(req,data);
} else { // 当请求返回错误
top.frames('chatqq').myAlert("There was a problem loading data :\n"
+ req.status+ "/" + req.statusText);
if (callback2) { //当定义了失败回调函数时,执行失败回调函数
callback2(req,data);
}
}
try { //删除XMLHTTP,释放资源
delete req;
req = null;
} catch (e) {}
}
}
//如果以POST方式回发服务器
if(method=="POST") {
req.open("POST", url, true);
req.send(data);
}else { //以GET方式请求
req.open("GET", url, true);
req.send(null);
}
Request.reqList.push(req);
return req; //返回XMLHTTP
}
//全部清除XMLHTTP数组元素,释放资源
Request.clearReqList = function() {
var ln = Request.reqList.length;
for(var i=0; i<ln; i++) {
var req = Request.reqList[i]; //获得一个请求
if (req) {
try{
req.close();
delete req;
} catch(e) {}
}
}
Request.reqList = [];
}
//进一步封装XMLHTTP以POST方式发送请求时的代码
//clear:是否清除XMLHTTP数组的所有元素;其他参数的意义参见Request.send
Request.sendPOST = function(url, data, callback, clear, callback2) {
if(clear)
Request.clearReqList();
Request.send(url, "POST", callback, data, true, callback2);
}
//进一步封装XMLHTTP以GET方式发送请求时的代码
Request.sendGET = function(url, callback, args, clear, callback2) {
if(clear)
Request.clearReqList();
return Request.send(url, "GET", callback, args, false, callback2);
}
// 关闭页面的一个元素
function closeme(name) {
try{
div =document.getElementById(name);
if(div) {
document.body.removeChild(div);
delete div;
div = null;
}
} catch(E) {}
}
18.4.2 消息提示框myAlert
读者应该知道javascript中的alert()方法可以弹出一个消息提示框,但alert弹出来的提示框界面不美观,不够人性化。此处就是要仿照Windows关机效果封装出一个myAlert()方法用来替代alert,myAlert()的运行效果如图18.9所示。

图18.9 myAlert()运行效果
在如图18.9中可以看出,myAlert运行后的界面主要由以下几部分构成:遮罩层,就是显示为银灰色的那个层,它用来遮盖整个浏览器窗口;标题栏,蓝黑色的那部分,当鼠标单击这个区域时,整个提示框关闭;消息框,白色区域的那部分,用来显示提示信息。myAlert的使用方法和alert完全相同,把提示消息作为参数传给myAlert即可。其实现代码如下:
//弹出具有亲和力的提示窗口
function myAlert(str){
var msgw,msgh,bordercolor;
msgw=400; //提示窗口的宽度
msgh=100; //提示窗口的高度
titleheight=25 //提示窗口标题高度
bordercolor="#336699"; //提示窗口的边框颜色
titlecolor="#99CCFF"; //提示窗口的标题颜色
var sWidth,sHeight; //顶层窗体的宽和高
sWidth=top.document.body.offsetWidth; //遮罩层的宽度
sHeight=top.document.body.offsetHeight; //遮罩层的高度
if(sHeight<top.screen.height)
{
sHeight=top.screen.height; //窗口滚动条位置不在0点,则遮罩层高度为屏幕高度
}
//遮罩层
var bgObj=top.document.createElement("div");
bgObj.setAttribute('id','bgDiv');
bgObj.style.position="absolute";
bgObj.style.top="0";
bgObj.style.background="#777";
//设置滤镜
bgObj.style.filter="progid:DXImageTransform.Microsoft.Alpha(style=3,opacity=
25,finishOpacity=75";
bgObj.style.opacity="0.6";
bgObj.style.left="0";
bgObj.style.width=sWidth + "px";
bgObj.style.height=sHeight + "px";
bgObj.style.zIndex = "10000";
top.document.body.appendChild(bgObj); //添加进窗口中
//设置消息框的一些属性
var msgObj=top.document.createElement("div")
msgObj.setAttribute("id","msgDiv");
msgObj.setAttribute("align","center");
msgObj.style.background="white";
msgObj.style.border="1px solid " + bordercolor;
msgObj.style.position = "absolute";
msgObj.style.left = "50%";
msgObj.style.top = "50%";
msgObj.style.font="12px 宋体";
msgObj.style.marginLeft = "-225px" ;
msgObj.style.marginTop = -75+top.document.documentElement.scrollTop+"px";
msgObj.style.width = msgw + "px";
msgObj.style.height =msgh + "px";
msgObj.style.textAlign = "center";
msgObj.style.lineHeight = (msgh-titleheight) + "px";
msgObj.style.zIndex = "10001";
//消息框的标题栏
var title=top.document.createElement("h4");
title.setAttribute("id","msgTitle");
title.setAttribute("align","right");
title.style.margin="0";
title.style.padding="3px";
title.style.background=bordercolor;
title.style.filter="progid:DXImageTransform.Microsoft.Alpha(startX=20,
startY=20,
finishX=100,
finishY=100,style=1,opacity=75,finishOpacity=100);";
title.style.opacity="0.75";
title.style.border="1px solid " + bordercolor;
title.style.height="18px";
title.style.font="12px 宋体";
title.style.color="white";
title.style.cursor="pointer";
title.innerText="关闭";
title.onclick=function(){ //单击标题栏,关掉遮罩层
top.document.body.removeChild(bgObj);
top.document.getElementById("msgDiv").removeChild(title);
top.document.body.removeChild(msgObj);
}
top.document.body.style.overflow = "hidden";
top.document.body.appendChild(msgObj); //添加高亮层
top.document.getElementById("msgDiv").appendChild(title);
var txt=top.document.createElement("p"); //显示提示内容的容器
txt.style.margin="1em 0"
txt.setAttribute("id","msgTxt"); //设置id
txt.innerHTML=str; //设置提示内容
top.document.getElementById("msgDiv").appendChild(txt); //添加提示内容
}
18.4.3 QQ菜单
实现一个类似QQ样式的菜单,如图18.10所示。QQ菜单的每个菜单项都由一个菜单头,一个菜单体组成。当单击菜单头时,与之对应的菜单体缓慢展开。
qq.js文件里的脚本用来实现这种效果。当单击菜单头后,触发showme()方法,根据当前状态执行缓慢上移或下移动作。其中moveup()方法控制菜单上移动作,movedown方法控制菜单下移动作。Moveme()方法是用来根据情况调用上移和下移的方法。QQ菜单的具体实现代码如下:

图18.10 类似QQ样式的菜单
var headHeight = 22; //每个菜单的标题栏的高度
var bodyHeight = 390; //每个菜单显示部分的高度
var objcount = 5; //菜单的数量
var step = 10; //菜单条缓慢上升或下降的步幅
var moving = false; //标志菜单是否在上升下降的移动
//显示QQ菜单的某一个菜单。obj1:菜单内容控件的id;obj2菜单标题显示控件的id
function showme(obj1, obj2)
{
if(moving) //如果正在移动,则返回
return;
moving = true; //移动标志
//改变CSS
for(i=0;i<document.all.tags('td').length;i++)
if(document.all.tags('td')[i].className.indexOf('headtd') == 0)
document.all.tags('td')[i].className = 'headtd1';
obj2.className = 'headtd2';
moveme(obj1); //开始移动
}
//移动。obj代表要显示的菜单的内容id
function moveme(obj)
{
idnumber = parseInt(obj.id.substr(4)); //菜单的索引
objtop = headHeight * (idnumber - 1); //菜单顶部的y坐标
objbuttom = bodyHeight + headHeight * (idnumber - 2); //菜单底步的y坐标
currenttop = parseInt(obj.style.top); //当前顶部坐标
if(currenttop >= objbuttom) //菜单上升
{
countid = 1;
for(i=0;i<document.all.tags('div').length;i++) //遍历div,找到当前运动
//的菜单项
if(document.all.tags('div')[i].id == 'item'+countid+'body')
{
obj = document.all.tags('div')[i];
objtop = headHeight * (countid - 1); //将要移动到的位置
if(countid == idnumber)
{
moveup(obj,objtop,false);
break;
}
else
moveup(obj,objtop,false);
countid++;
}
}
else if ((currenttop <= objtop) && (idnumber < objcount)) //菜单下降
{
idnumber++;
countid = objcount;
for(i=document.all.tags('div').length-1;i>=0;i--) //遍历div,找到当前
//运动的菜单项
if(document.all.tags('div')[i].id == 'item'+countid+'body')
{
obj = document.all.tags('div')[i];
objbuttom = bodyHeight + headHeight * (countid - 2);
//将要移动到的位置
if(countid == idnumber)
{
movedown(obj,objbuttom,false);
break;
}
else
movedown(obj,objbuttom,true);
countid--;
}
}
}
//向上移动
function moveup(obj,objtop,ismove)
{
currenttop = parseInt(obj.style.top); //当前y坐标值
if(currenttop > objtop)
{
obj.style.top = currenttop - step; //y坐标值减小
setTimeout('moveup('+obj.id+','+objtop+','+ismove+')',1) //延时执行,实
//现动画上升效果
return;
}
moving = ismove;
}
//向下移动
function movedown(obj,objbuttom,ismove)
{
currenttop = parseInt(obj.style.top); //当前y坐标值
if(currenttop < objbuttom)
{
obj.style.top = currenttop + step; //y坐标值减小
setTimeout('movedown('+obj.id+','+objbuttom+','+ismove+')',1)
//延时执行,实现动画下降效果
return;
}
moving = ismove;
}
18.4.4 拖动类
用鼠标光标来拖动某个图层在浏览器窗口移动是当前非常流行的一种技术,本部分介绍的代码就是用来实现这个功能的。整个功能的实现都封装在一个Drag类中。当需要拖动某个对象时,首先调用Drag类的init()方法用来初始化Drag类。然后调用start()方法,拖动开始。拖动过程中不断的调用drag()方法,实现图层移动效果。当鼠标弹起时,调用end()事件,拖动过程结束。拖动类Drag的具体实现代码如下:
//拖动对象
var Drag={
"obj":null,
"init":function(a, aRoot){//初始化
a.onmousedown=Drag.start; //鼠标按下的事件
a.root = aRoot; //拖动条
//默认位置
if(isNaN(parseInt(a.root.style.left)))a.root.style.left="0px";
if(isNaN(parseInt(a.root.style.top)))a.root.style.top="0px";
//定义了拖动开始,正在拖动,拖动结束几个方法
a.root.onDragStart=new Function();
a.root.onDragEnd=new Function();
a.root.onDrag=new Function();
},
"start":function(a){ //拖动开始
top.event.cancelBubble = true; //禁止事件冒泡
var b=Drag.obj=this; //赋值
a=Drag.fixE(a); //修正一些意外情况
var c=parseInt(b.root.style.top); //top
var d=parseInt(b.root.style.left); //left
b.root.onDragStart(d,c,a.clientX,a.clientY);
b.lastMouseX=a.clientX; //记录下最初的位置
b.lastMouseY=a.clientY;
top.document.onmousemove=Drag.drag; //挂载正在拖动的事件函数
top.document.onmouseup=Drag.end; //挂载拖动结束的函数
return false;
},
"drag":function(a){ //正在拖动
a=Drag.fixE(a); //修正意外
var b=Drag.obj; //拖动条对象
var c=a.clientY; //记录下被拖对象的位置
var d=a.clientX;
var e=parseInt(b.root.style.top); //拖动条的位置
var f=parseInt(b.root.style.left);
var h,g;
h=f+d-b.lastMouseX;
g=e+c-b.lastMouseY;
b.root.style.left=h+"px"; //拖动条的新位置
b.root.style.top=g+"px";
b.lastMouseX=d; //记录下上次的鼠标光标位置
b.lastMouseY=c;
b.root.onDrag(h,g,a.clientX,a.clientY);
return false;
},
"end":function(){//拖动结束
top.document.onmousemove=null; //把挂载的事件取消
top.document.onmouseup=null;
//拖动结束
Drag.obj.root.onDragEnd(parseInt(Drag.obj.root.style.left),parseInt
(Drag.obj.root.style.top));
Drag.obj=null; //清空对象
},
"fixE":function(a){ //修正undefined的问题
if(typeof a=="undefined")a=top.event; //最顶层窗体的事件
if(typeof a.layerX=="undefined")a.layerX=a.offsetX; //x轴位置
if(typeof a.layerY=="undefined")a.layerY=a.offsetY; //y轴位置
return a;
}
};
18.4.5 HTML编辑器相关的HTML代码
用户聊天时,可以发出不同颜色、不同字体、不同字体大小的文字内容,实现这个功能是添加了一个小的HTML编辑器,编辑器的运行效果如图18.11所示。
编辑器的基本原理就是把一个iframe窗体作为一个编辑器窗口,把它的designMode设置为On,这样所有可以在浏览器窗口中显示的元素都可以在其中编辑。编辑器的HTML代码如下:
<table style="position: absolute; left: 8px; width: 397px; top: 239px;">
<textarea name="content" id="content" style="display: none; width: 380px; word-bread: break-all;
word-wrap: break-word; scrollbar-face-color: #DBEBFE; scrollbar-shadow- color: #B8D6FA;
scrollbar-highlight-color: #FFFFFF; scrollbar-3dlight-color: #DBEBFE; scrollbar-darkshadow-color: #458CE4;
scrollbar-track-color: #FFFFFF; scrollbar-arrow-color: #458CE4"></textarea>
<tr>
<td width="100">
<select name="font_name" style="width: 100" id="font_name" onchange=
"FontName(this.options[this.selectedIndex].value)">
<option class="heading" selected>字体</option>
<option value="宋体">宋体</option>
<option value="黑体">黑体</option>
<option value="楷体_GB2312" style="margin-top: 0px;">楷体</option>
<option value="仿宋_GB2312">仿宋</option>
<option value="隶书">隶书</option>
<option value="幼圆">幼圆</option>
<option value="新宋体">新宋体</option>
<option value="细明体">细明体</option>
<option value="Arial">Arial</option>
<option value="Arial Black">Arial Black</option>
<option value="Arial Narrow">Arial Narrow</option>
<option value="Bradley Hand ITC">Bradley Hand ITC</option>
<option value="Brush Script MT">Brush Script MT</option>
<option value="Century Gothic">Century Gothic</option>
<option value="Comic Sans MS">Comic Sans MS</option>
<option value="Courier">Courier</option>
<option value="Courier New">Courier New</option>
<option value="MS Sans Serif">MS Sans Serif</option>
<option value="Script">Script</option>
<option value="System">System</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Viner Hand ITC">Viner Hand ITC</option>
<option value="Verdana">Verdana</option>
<option value="Wide Latin">Wide Latin</option>
<option value="Wingdings">Wingdings</option>
</select>
</td>
<td width="60">
<select name="font_size" id="font_size" onchange="FontSize(this.op-
tions[this.selectedIndex].value)">
<option value="1">字号</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
</td>
<td width="19" onclick="FontColor()" onmouseover="this.className='Hand_On'"
onmouseout="this.className='Hand_Off'"
class='Hand_Off'>
<img src="Editor/images/fgcolor.gif" width="16" height="16" border="0" alt="字体颜色"></td>
<td width="21" onclick="BackColor()" onmouseover="this.className='Hand_ On'" onmouseout="this.className='Hand_Off'"
class='Hand_Off'>
<img src="Editor/images/fbcolor.gif" width="16" height="16" border= "0" alt="背景颜色"></td>
<td width="17" onclick="bold()" onmouseover="this.className='Hand_On'" onmouseout="this.className='Hand_Off'"
class='Hand_Off'>
<img src="Editor/images/bold.gif" width="16" height="16" border="0" alt="加粗"></td>
<td width="17" onclick="italic()" onmouseover="this.className='Hand_On'" onmouseout="this.className='Hand_Off'"
class='Hand_Off'>
<img src="Editor/images/italic.gif" width="16" height="16" border="0" alt="倾斜"></td>
<td width="18" onclick="underline()" onmouseover="this.className='Hand_ On'" onmouseout="this.className='Hand_Off'"
class='Hand_Off'>
<img src="Editor/images/underline.gif" width="16" height="16" border= "0" alt="下划线"></td>
<td width="112" class='Hand_Off'>
</td>
</tr>
<tr>
<td colspan="9" style="width: 390px; height: 110px;">
<script type="text/javascript" language="JavaScript">Editor(document. getElementById("content").value);</script>
</td>
</tr>
</table>
18.4.6 HTML编辑器相关的脚本代码
由上节的编辑器的HTML代码可以看出,编辑器的初始化方法是Editor()。这个方法的作用就是输出一个iframe,然后在iframe中记录下需要显示的内容。getContent()方法是用来得到HTML编辑器中的内容,在这个方法中对文字进行各种格式处理。与HTML编辑器相关的脚本代码如下:
var oEditor; //编辑器窗口
function Editor(){ //编辑器窗口初始化
document.write('<iframe name="wrEditor" id="wrEditor" width="100%" frameBorder= "0" height="100%" src="about:blank"></iframe>');
oEditor = document.getElementById("wrEditor").contentWindow;//找到编辑器窗口
var strHtml = '<html><style>body{font-size:14px; margin:2px;}\ntd, a{color:#0000FF; font-size:14px;}</style><body style="scrollbar-face-color: #DBEBFE; scrollbar-shadow- color: #B8D6FA; scrollbar-highlight-color: #FFFFFF; scrollbar-3dlight-color: #DBEBFE; scrollbar-darkshadow-color:#458CE4; scrollbar-track-color: #FFFFFF; scrollbar-arrow- color: #458CE4"></body></html>';
oEditor.document.open();
oEditor.document.write(strHtml); //写入内容
oEditor.document.close();
oEditor.document.designMode="On"; //设计模式
oEditor.focus();
}
//文字加粗
function bold(){
var sText = oEditor.document.selection.createRange(); //选中
if(sText!=""){
oEditor.document.execCommand("bold");
}
}
//倾斜
function italic(){
var sText = oEditor.document.selection.createRange();
if(sText!=""){
oEditor.document.execCommand("italic");
}
}
//下划线
function underline(){
var sText = oEditor.document.selection.createRange();
if(sText!=""){
oEditor.document.execCommand("underline");
}
}
//水平对齐方式,参数:left/center/right
function ralign(aStr){
switch(aStr){
case "left": //左对齐
oEditor.document.execCommand("JustifyLeft");
break;
case "center": //居中对齐
oEditor.document.execCommand("JustifyCenter");
break;
case "right": //右对齐
oEditor.document.execCommand("JustifyRight");
break;
default:
return false;
}
}
//字体颜色
function FontColor(){
//显示颜色选择框
var arr = showModalDialog("Editor/include/selcolor.htm", window, "dialogWidth: 300px; dialogHeight:300px; status:0; help:0");
if(arr)
{
var sText = oEditor.document.selection.createRange(); //选中
if(sText){
oEditor.document.execCommand("ForeColor", "false", arr);
}
}
oEditor.focus();
}
//字体背景颜色
function BackColor(){
//颜色选择器
var arr = showModalDialog("Editor/include/selcolor.htm", window, "dialog Width:300px; dialogHeight:300px; status:0; help:0");
if(arr)
{
var sText = oEditor.document.selection.createRange();
if(sText){
oEditor.document.execCommand("BackColor", "false", arr);
}
}
oEditor.focus();
}
//设置字体大小
function FontSize(value){
var sText = oEditor.document.selection.createRange();
if(sText){
oEditor.document.execCommand("FontSize", "false", value);
}
}
//设置字体样式
function FontName(value){
var sText = oEditor.document.selection.createRange();
if(sText){
oEditor.document.execCommand("FontName", "false", value);
}
}
//获得编辑器的内容
function getContent()
{
return correctUrl(oEditor.document.body.innerHTML);
}
//对超链接进行一些处理
function correctUrl(cont)
{
var regExp;
regExp = /<a([^>]*) href\s*=\s*([^\s|>]*)([^>]*)/gi
cont = cont.replace(regExp, "<a href=$2 target=\"_blank\"");
regExp = /<a([^>]*)><\/a>/gi
cont = cont.replace(regExp, "");
return cont;
}







