Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

NodeJS简易聊天室(客户端)

  昨天说了服务器端的程序,有些东西是在前端程序中处理的,光看后端程序看不出个所以然来。今天就补上前端程序的部分。先丢个下载地址吧“NodeJS简易聊天室”,要是把地址丢文章最后可能会被忽略掉。
  好了,现在来说前端代码。至于HTML和CSS部分我就不解释了,都是基础的东西,没用到什么高端的技术。不过为了方便后面的程序理解我还是先贴出来吧。
<!DOCTYPE html>
<style>
body {font-family:Consolas,微软雅黑;}
body * {padding:0px;margin:0px;}
a {text-decoration:none;}
#panel,#form,#text {border:1px solid #CCC;}
#panel {overflow:auto;width:300px;height:200px;}
#panel dl {padding:5px 8px;margin-bottom:-5px;font-size:14px;}
#panel dd {display:block;padding-left:1em;word-wrap:break-word;}
#form {padding:10px;width:280px;margin-top:10px;}
#text {padding:2px;width:200px;}
#send {margin-left:10px;color:#333;}
</style>
<div id="panel"></div>
<div id="form">
  <input id="text" autocomplete="off"/>
  <a id="send" href="JavaScript:;">发送</a>
</div>
  下面是程序的全局部分 //创建XHR类
function XHR(){
  return -[1,]
    ?new XMLHttpRequest()
    :new ActiveXObject("Microsoft.XMLHTTP");
};
//定义长轮询过程
function longPolling(ETag,f){
  if(!longPolling.xhr)longPolling.xhr=new XHR;
  xhr=longPolling.xhr;
  if(f==null)f=ETag,ETag="";
  xhr.open("POST","receive",true);
  xhr.onreadystatechange=function(){
    if(xhr.readyState==4){
      if(xhr.status==200)f(xhr.responseText);
      longPolling(xhr.getResponseHeader("ETag"),f);
    };
  };
  xhr.setRequestHeader("If-None-Match",ETag);
  xhr.send();
};
//获取自身IP
var IP=document.cookie.match(/ip=([\d.]+)/)[1];
  开头部分的XHR其实只是一个函数,它的功能是在不同环境下创建XHR对象而已。只是返回的是XHR对象,所以它更像一个类,在逻辑上我把它当作类了。在后面的代码中使用它时候也会用new。虽然没必要,但是我觉得那样逻辑上比较通顺吧。这个函数里面的“-[1,]”是用来判断IE8-的,这是很古老的代码了,百度一下能找到,我就不解释原理了。
  接着是定义一个用来长轮询的函数,函数体中的前两句是判断XHR对象是否创建,没创建则创建它。其实这个放在全局中定义就不用判断了,不过为了降低程序的耦合度,直接把它放入函数中定义。第三行是判断参数的,相当于让这个函数实现多载。然后就是Ajax部分了,它访问的是服务器的receive这个页面,还记得昨天的文章中在服务器上定义的接口吗?这个XHR对象就是访问这个接口的。这里还要注意很关键的一点是必须使用POST方法来通信。因为GET方法在多开的情况下会被浏览器劫持而无法连接到服务器上,关于这个问题可以看看之前的文章“各浏览器对GET方法的处理差异”。接着,这个Ajax会发送一个If-None-Match,也就是服务器返回的ETag。我没有把这个工作交给浏览器,而是自己用代码控制ETag。这就意味着这个ETag是程序解析的不是浏览器解析的。所以在昨天的文章中才说用LastModified也可以,因为是自己解析的,我们完全可以让LastModified精确到毫秒。当这XHR对象收到服务器的数据时会调用f这个函数。这个函数是通过参数传入的。f处理完数据之后又会调用longPolling,实现了间接递归,这样轮询就会一直进行。
  接着是获取自己的IP,这个可以在Cookie中找到。因为服务器程序在输出页面的时候写了这个Cookie。这个IP用来判断哪个消息是自己的,自己的消息和别人的消息用颜色区别开,就像QQ那样。
  下面是文档载入完成后调用的代码 window.onload=function(){
  //开始长轮询
  longPolling(function(e){
    e=eval("("+e+")");
    var i=0,o,dl,dt,dd,t;
    while(o=e[i++]){
      t=new Date(o.time*1);
      t=[t.getHours(),t.getMinutes(),t.getSeconds()];
      dl=document.createElement("dl");
      dt=document.createElement("dt");
      dd=document.createElement("dd");
      dt.appendChild(document.createTextNode(
        o.ip+" "+t.join(":")
      ));
      dt.style.color=o.ip==IP?"green":"blue";
      dd.appendChild(document.createTextNode(o.text));
      dl.appendChild(dt),dl.appendChild(dd);
      panel.appendChild(dl);
      panel.scrollTop=1E8;
    };
  });
  //回车键发送
  text.onkeyup=function(e){
    if((e||event).keyCode==13)send.click();
  };
  //发送数据
  send.onclick=function(){
    if(text.value=="")return;
    var xhr=new XHR;
    xhr.open("POST","send",true);
    var i,s=[],o={text:text.value};
    for(i in o)s.push(i+"="+encodeURIComponent(o[i]));
    xhr.send(s.join("&"));
    text.value="";
  };
};
  longPolling就是在这个代码中使用的,使用的时候传入一个回调函数。这个函数就是之前定义longPolling时里面的f。这个回调函数没啥好说的,只是一些普通的页面操作而已。长轮询从服务器取得数据后就通过这个函数在页面上生成相应的元素来显示数据。这里调用的longPolling只有一个参数,这就意味着ETag为空。这样第一次发送长轮询请求时就不带ETag,服务器没收到ETag就会把所有消息队列里的消息发送一份到客户端上。这样就是网页刷新也不会让聊天中的数据丢失了,而且新进来的人也可以看到之前在聊的是什么。
  接着是文本框的onkeyup事件,用来回车键发送数据。没啥好说的,跳过。
  最后是发送数据的部分,也是使用Ajax。从页面上取得用户输入的东西,包装成URL参数格式POST过去。其实如果只是实现简单的文字通信完全可以直接POST文字不用做任何包装,只要在服务器端解析时调整就好了。包装数据的好处是容易扩展,如果这个聊天程序要加入字体大小调整之类的一些列服务可以一起包装发送。
  到这里这个聊天程序的代码就全部解释完了,有什么不理解的地方或有什么更好的想法可以交流下~
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^