股票案例
我们要做的是股票的案例,它能够无刷新地更新股票的数据。当鼠标移动到具体的股票中,它会显示具体的信息。
我们首先来看一下要做出来的效果:

服务器端分析
首先,从效果图我们可以看见很多股票基本信息:昨天收盘价、今天开盘价、最高价、最低价、当前价格、涨幅。这些信息我们用一个类来描述出来。
我们发现数据是定时刷新的,于是我们需要一个定时器。
服务器端的数据和客户端交互,我们使用JSON吧
服务器端代码
Stock股票类的代码
-
股票基本信息:
1private String id; 2 3private String name; 4 5private double yesterday; 6 7private double today ; 8 9private double highest; 10 11private double lowest; 12 13private double current; 14 15private String range ; 16 17//各种setter和getter -
Stock的构造函数:
1/** * id,name,yesterday这三个参数都是固定的,其他的属性都是可变的。 * 因此我们构造函数就传入这三个值 * */ 2public Stock(String id, String name, double yesterday) { 3 this.id = id; 4 this.name = name; 5 this.yesterday = yesterday; 6 7 //把开盘价设定为-1,后面在定时器计算出来的随机数,如果发现开盘价是-1,就设置第一次的随机数为开盘价 8 this.today = -1; 9 10 //把最高、最低、当前的价格都暂且设置成昨天的开盘价,后面我们可以变化的 11 this.highest = yesterday; 12 this.current = yesterday; 13 this.lowest = yesterday; 14 15} -
setCurrent()方法代码:
/** * 每次设置当前价钱的时候,最高、最低、涨幅都应该随着当前价钱而变化的 */ public void setCurrent(double current) { //计算出涨幅或跌幅 double range = (current - this.yesterday) / this.yesterday; //设置涨幅和跌幅不能超过10%,当前的价格只能是昨天开盘价的1.1倍或0.9倍 //当前价格应该是两位小数 DecimalFormat formatPrice = new DecimalFormat("#.00"); if (range > 0.1) { current = Double.parseDouble(formatPrice.format(this.yesterday * 1.1)); } if (range < -0.1) { current = Double.parseDouble(formatPrice.format(this.yesterday * 0.9)); } this.current = current; //如果今天开盘价没设定,那么就将第一次的当前价作为今天的开盘价 if (this.today == -1) { this.today = this.current; } //比较最大值和最小值 if (this.current > this.highest) { this.highest = this.current; } if (this.current < this.lowest) { this.lowest = this.current; } //格式化涨幅的字符串,整数两位,小数两位 DecimalFormat formatRange = new DecimalFormat("##.##%"); this.range = formatRange.format(range); }
Servlet的代码
-
init()初始化代码:
1/** * 重写init()方法,加入一些配置内容 */ 2@Override 3public void init(ServletConfig config) throws ServletException { 4 5 map = new HashMap<>(); 6 7 //新建几只固定的股票 8 final Stock zhong = new Stock("1", "百度", 1110.1); 9 final Stock fu = new Stock("2", "阿里", 222.2); 10 final Stock cheng = new Stock("3", "腾讯", 333.3); 11 final Stock ou = new Stock("4", "谷歌", 1133.5); 12 13 //添加到容器中 14 map.put("1", zhong); 15 map.put("2", fu); 16 map.put("3", cheng); 17 map.put("4", ou); 18 19 //生成随机数 20 final Random random = new Random(); 21 22 //格式化生成的随机数 23 final DecimalFormat format = new DecimalFormat("#.00"); 24 25 //Servlet被启动后1秒开始,每两秒扫描一次 26 27 timer = new Timer(); 28 timer.schedule(new TimerTask() { 29 @Override 30 public void run() { 31 32 double baidu = random.nextDouble() * 1.1; 33 double ali = random.nextDouble() * 2; 34 double tengxun = random.nextDouble() * 0.3; 35 double geogle = random.nextDouble() * 4; 36 37 //概率大致都是50%,我们用来做正负 38 if (random.nextBoolean()) { 39 baidu = 0 - baidu; 40 } 41 if (random.nextBoolean()) { 42 ali = 0 - ali; 43 } 44 45 if (random.nextBoolean()) { 46 tengxun = 0 - tengxun; 47 } 48 if (random.nextBoolean()) { 49 geogle = 0 - geogle; 50 } 51 52 //设置它们的当前价格 53 zhong.setCurrent(Double.parseDouble(format.format(zhong.getCurrent()+baidu))); 54 fu.setCurrent(Double.parseDouble(format.format(fu.getCurrent()+ali))); 55 cheng.setCurrent(Double.parseDouble(format.format(cheng.getCurrent()+tengxun))); 56 ou.setCurrent(Double.parseDouble(format.format(ou.getCurrent()+geogle))); 57 58 } 59 }, 1000, 2000); 60 61 62} -
服务器一启动就应该初始化Servlet
<servlet> <servlet-name>Refresh</servlet-name> <servlet-class>Refresh</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Refresh</servlet-name> <url-pattern>/Refresh</url-pattern> </servlet-mapping>
-
doPost()代码:
1 //封装成JSON格式,返回给浏览器 2 StringBuffer buffer = new StringBuffer(); 3 4 5 //这里我们拼接成4个对象 6 buffer.append("({"); 7 for (Map.Entry<String, Stock> entry : map.entrySet()) { 8 String id = entry.getKey(); 9 Stock stock = entry.getValue(); 10 11 buffer.append(id).append(":{yesterday:").append(stock.getYesterday()).append(",today:").append(stock.getToday()).append(",high:").append(stock.getHighest()).append(",low:").append(stock.getLowest()).append(",current:").append(stock.getCurrent()).append(",range:'").append(stock.getRange()).append("'}").append(","); 12 13 } 14 //消除最后一个逗号 15 buffer.deleteCharAt(buffer.lastIndexOf(",")); 16 17 //最后补上括号 18 buffer.append("})"); 19 20 //返回给浏览器 21 response.getWriter().write(buffer.toString()); -
拼接成的JSON数据:
({ 3:{yesterday:333.3,today:333.48,high:333.48,low:333.3,current:333.48,range:'0.05%'}, 2:{yesterday:222.2,today:223.46,high:223.46,low:222.2,current:223.46,range:'0.57%'}, 1:{yesterday:1110.1,today:1109.73,high:1110.1,low:1109.73,current:1109.73,range:'-0.03%'}, 4:{yesterday:1133.5,today:1135.49,high:1135.49,low:1133.5,current:1135.49,range:'0.18%'} })
客户端分析之一
客户端要做的就是显示数据,每隔两秒就和服务器进行一次交互
- 用到Ajax和setInterval()方法
html代码
使用div嵌套span和a标签来进行显示,span装载的就是服务端返回json的current数据
1<body onload="show()"> 2 3<div> 4 <a href="#">百度:</a> 5 <span id="1"></span> 6</div> 7<div> 8 <a href="#">阿里巴巴:</a> 9 <span id="2"></span> 10</div> 11<div> 12 <a href="#">腾讯:</a> 13 <span id="3"></span> 14</div> 15<div> 16 <a href="#">谷歌:</a> 17 <span id="4"></span> 18</div> 19 20 21</body>
javaScript代码
-
解析JSON,并设置span的内容
function show() { getStock(); //每两秒就取一次数据 setInterval(getStock, 2000); } var httpRequest; function getStock() { //力求是最新的响应数据,如果存在httpRequest,那么将上次的httpRequest终止 if(httpRequest) { httpRequest.abort(); } httpRequest= new XMLHttpRequest(); httpRequest.open("GET", "Refresh", true); httpRequest.onreadystatechange = callBackFunction; httpRequest.send(null); } function callBackFunction() { if(httpRequest.readyState==4) { if(httpRequest.status==200) { //得到服务器端返回的JSON数据 var text = httpRequest.responseText; //解析成JavaScript对象 var json = eval(text); //遍历出每个JSON对象【也就是json的id】 for(var id in json) { //得到每个stock对象 var stock = json[id]; //将当前的价格设置到span节点里面 document.getElementById(id).innerHTML = stock.current; //比较当前价格和昨天开盘价格,如果大于就是红色,小于就是绿色 if(stock.current>stock.yesterday) { document.getElementById(id).style.color = 'red'; }else { document.getElementById(id).style.color = 'green'; } } } } }
-
效果

客户端分析之二
当鼠标移动到具体的股票超链接的时候,会显示具体的数据,并且数据是动态的
- 在超链接上绑定事件
- 取出和服务器交互的数据,显示在页面上
html代码:
绑定事件,只要鼠标移动到超链接上就触发事件
1<body onload="show()"> 2 3<div> 4 <a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">百度:</a> 5 <span id="1"></span> 6</div> 7<div> 8 <a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">阿里巴巴:</a> 9 <span id="2"></span> 10</div> 11<div> 12 <a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">腾讯:</a> 13 <span id="3"></span> 14</div> 15<div> 16 <a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">谷歌:</a> 17 <span id="4"></span> 18</div> 19 20<div id="toolTip"> 21 <div> 22 昨收:<span id="yesterday"></span> 23 </div> 24 25 <div> 26 今收:<span id="today"></span> 27 </div> 28 29 <div> 30 最低:<span id="low"></span> 31 </div> 32 <div> 33 当前:<span id="current"></span> 34 </div> 35 <div> 36 最高:<span id="high"></span> 37 </div> 38 <div> 39 涨幅:<span id="range"></span> 40 </div> 41</div> 42 43 44</body>
css代码
详细框的信息默认是隐藏的
<style type="text/css"> #toolTip { border: 1px solid #000; width: 150px; position: absolute; display: none; } </style>
javaScript代码
得到交互的数据,设置span里面的值
1 function update() { 2 3 var stock = json[sid]; 4 5 //得到相对应的控件 6 var yesterday = document.getElementById("yesterday"); 7 var today = document.getElementById("today"); 8 var low = document.getElementById("low"); 9 var high = document.getElementById("high"); 10 var range = document.getElementById("range"); 11 var current = document.getElementById("current"); 12 13 //设置具体信息的值 14 15 high.innerHTML = stock.high; 16 range.innerHTML = stock.range; 17 current.innerHTML = stock.current; 18 yesterday.innerHTML = stock.yesterday; 19 today.innerHTML = stock.today; 20 low.innerHTML = stock.low; 21 22 //如果数值比昨天开盘价低,反则就是红色 23 if (stock.today > stock.yesterday) { 24 today.style.color = 'red'; 25 } else { 26 today.style.color = 'green'; 27 } 28 29 if (stock.low > stock.yesterday) { 30 low.style.color = 'red'; 31 } else { 32 low.style.color = 'green'; 33 } 34 if (stock.high > stock.yesterday) { 35 high.style.color = 'red'; 36 } else { 37 high.style.color = 'green'; 38 } 39 40 //如果现在的价格比昨天开盘高,那么涨幅是红色 41 if (stock.current > stock.yesterday) { 42 range.style.color = 'red'; 43 current.style.color = 'red'; 44 } else { 45 range.style.color = 'green'; 46 current.style.color = 'green'; 47 } 48 }
只有鼠标移到超链接上,才明确id的值是多少!
1 function callBackFunction() { 2 if (httpRequest.readyState == 4) { 3 if (httpRequest.status == 200) { 4 5 //得到服务器端返回的JSON数据 6 json= eval(httpRequest.responseText); 7 8 //更新详细框的数据,当鼠标移动到超链接上才确定有id,于是判断有没有id 9 if(sid) { 10 update(); 11 } 12 13 //遍历出每个JSON对象【也就是json的id】 14 for (var id in json) { 15 16 //得到每个stock对象 17 var stock = json[id]; 18 19 //将当前的价格设置到span节点里面 20 document.getElementById(id).innerHTML = stock.current; 21 22 //比较当前价格和昨天开盘价格,如果大于就是红色,小于就是绿色 23 if (stock.current > stock.yesterday) { 24 document.getElementById(id).style.color = 'red'; 25 } else { 26 document.getElementById(id).style.color = 'green'; 27 28 } 29 } 30 31 32 } 33 } 34 } 35 36 function showTool(node) { 37 38 //得到鼠标移动到具体股票的id 39 sid = node.parentNode.getElementsByTagName("span")[0].id; 40 41 //把详细框框显示出来 42 document.getElementById("toolTip").style.display = 'block'; 43 44 } 45 function clearTool() { 46 document.getElementById("toolTip").style.display = 'none'; 47 } 48
最终效果:

总结要点
①:这是由AJAX来实现的,因为它无刷新的动态交互数据。
②:服务器端应该保存着股票的基本信息。于是乎,我们用一个类来装载着这些信息【信息之间的关系就不一一说明了,因为每个案例用的可能都不一样】
③:用到了DecimalFormat类来格式化小数变为自己想要的格式
④:使用HashMap来装载这些股票,使用Map集合主要是在客户端中,可以通过键来访问具体的股票,只要能访问到股票了,那么一切就好说了。
⑤:当然啦,装载股票的任务就交给init()方法,因为只需要装载一次。
⑥:我们会发现,股票的信息是不断会变化的,所以我们使用定时器和Random类来不断修改股票的信息
⑦:JavaScript和服务端交互使用AJAX,要么使用XML,要么就是JSON,这次我们采用的是JSON
⑧:JavaScript使用XMLHttpRequest对象得到Servlet返回给浏览器的JSON数据,解析JSON数据,变成是JavaScript对象
⑨:在页面上显示服务端带过来的数据,一般都是使用div来显示【块级】,用控件绑定id,在JavaScript中得到控件,填充数据。这样就是动态地修改页面的数据了。
⑩:浏览器想要不断地从服务端获取股票的数据,那么就需要不断地与服务端交互,解析JSON,填充数据…..这种我们可以通过setInterval()定时器来做
①①:想要修改字体的颜色,只要获取它的控件再style.color就可以修改了。
①②:鼠标移动到具体的股票链接的时候,会出现股票的详细信息时,这明显就是为超链接绑定了事件
①③:股票的详细信息用一个框框装载着,那么我们就在css中初始化这个框框,它平时是不显示出来的,只用在鼠标移到它那里的时候才显示,我们把display=“none”就行了。
①④:在响应事件的时候,我们需要知道用户是移动到哪一个超链接上,所以要获取得到具体的超链接id。知道id以后,我们就知道用户想要知道的股票是哪一个了。
①⑤:股票的信息也想要及时的更新,那么我们想把它抽取成一个方法,在AJAX回调方法中加入进去就行了。当然了,id和具体股票对象应该是全局的变量【这样才能够在别的方法中用到】