股票案例
我們要做的是股票的案例,它能夠無重新整理地更新股票的資料。當滑鼠移動到具體的股票中,它會顯示具體的資訊。
我們首先來看一下要做出來的效果:
伺服器端分析
首先,從效果圖我們可以看見很多股票基本資訊:昨天收盤價、今天開盤價、最高價、最低價、當前價格、漲幅。這些資訊我們用一個類來描述出來。
我們發現資料是定時重新整理的,於是我們需要一個定時器。
伺服器端的資料和客戶端互動,我們使用JSON吧
伺服器端程式碼
Stock股票類的程式碼
- 股票基本資訊:
private String id;
private String name;
private double yesterday;
private double today ;
private double highest;
private double lowest;
private double current;
private String range ;
//各種setter和getter
複製程式碼
- Stock的建構函式:
/**
* id,name,yesterday這三個引數都是固定的,其他的屬性都是可變的。
* 因此我們建構函式就傳入這三個值
* */
public Stock(String id, String name, double yesterday) {
this.id = id;
this.name = name;
this.yesterday = yesterday;
//把開盤價設定為-1,後面在定時器計算出來的隨機數,如果發現開盤價是-1,就設定第一次的隨機數為開盤價
this.today = -1;
//把最高、最低、當前的價格都暫且設定成昨天的開盤價,後面我們可以變化的
this.highest = yesterday;
this.current = yesterday;
this.lowest = yesterday;
}
複製程式碼
- 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()初始化程式碼:
/**
* 重寫init()方法,加入一些配置內容
*/
@Override
public void init(ServletConfig config) throws ServletException {
map = new HashMap<>();
//新建幾隻固定的股票
final Stock zhong = new Stock("1", "百度", 1110.1);
final Stock fu = new Stock("2", "阿里", 222.2);
final Stock cheng = new Stock("3", "騰訊", 333.3);
final Stock ou = new Stock("4", "谷歌", 1133.5);
//新增到容器中
map.put("1", zhong);
map.put("2", fu);
map.put("3", cheng);
map.put("4", ou);
//生成隨機數
final Random random = new Random();
//格式化生成的隨機數
final DecimalFormat format = new DecimalFormat("#.00");
//Servlet被啟動後1秒開始,每兩秒掃描一次
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
double baidu = random.nextDouble() * 1.1;
double ali = random.nextDouble() * 2;
double tengxun = random.nextDouble() * 0.3;
double geogle = random.nextDouble() * 4;
//概率大致都是50%,我們用來做正負
if (random.nextBoolean()) {
baidu = 0 - baidu;
}
if (random.nextBoolean()) {
ali = 0 - ali;
}
if (random.nextBoolean()) {
tengxun = 0 - tengxun;
}
if (random.nextBoolean()) {
geogle = 0 - geogle;
}
//設定它們的當前價格
zhong.setCurrent(Double.parseDouble(format.format(zhong.getCurrent()+baidu)));
fu.setCurrent(Double.parseDouble(format.format(fu.getCurrent()+ali)));
cheng.setCurrent(Double.parseDouble(format.format(cheng.getCurrent()+tengxun)));
ou.setCurrent(Double.parseDouble(format.format(ou.getCurrent()+geogle)));
}
}, 1000, 2000);
}
複製程式碼
- 伺服器一啟動就應該初始化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()程式碼:
//封裝成JSON格式,返回給瀏覽器
StringBuffer buffer = new StringBuffer();
//這裡我們拼接成4個物件
buffer.append("({");
for (Map.Entry<String, Stock> entry : map.entrySet()) {
String id = entry.getKey();
Stock stock = entry.getValue();
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(",");
}
//消除最後一個逗號
buffer.deleteCharAt(buffer.lastIndexOf(","));
//最後補上括號
buffer.append("})");
//返回給瀏覽器
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資料
<body onload="show()">
<div>
<a href="#">百度:</a>
<span id="1"></span>
</div>
<div>
<a href="#">阿里巴巴:</a>
<span id="2"></span>
</div>
<div>
<a href="#">騰訊:</a>
<span id="3"></span>
</div>
<div>
<a href="#">谷歌:</a>
<span id="4"></span>
</div>
</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程式碼:
繫結事件,只要滑鼠移動到超連結上就觸發事件
<body onload="show()">
<div>
<a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">百度:</a>
<span id="1"></span>
</div>
<div>
<a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">阿里巴巴:</a>
<span id="2"></span>
</div>
<div>
<a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">騰訊:</a>
<span id="3"></span>
</div>
<div>
<a href="#" onmouseover="showTool(this)" onmouseleave="clearTool()">谷歌:</a>
<span id="4"></span>
</div>
<div id="toolTip">
<div>
昨收:<span id="yesterday"></span>
</div>
<div>
今收:<span id="today"></span>
</div>
<div>
最低:<span id="low"></span>
</div>
<div>
當前:<span id="current"></span>
</div>
<div>
最高:<span id="high"></span>
</div>
<div>
漲幅:<span id="range"></span>
</div>
</div>
</body>
複製程式碼
css程式碼
詳細框的資訊預設是隱藏的
<style type="text/css">
#toolTip {
border: 1px solid #000;
width: 150px;
position: absolute;
display: none;
}
</style>
複製程式碼
javaScript程式碼
得到互動的資料,設定span裡面的值
function update() {
var stock = json[sid];
//得到相對應的控制元件
var yesterday = document.getElementById("yesterday");
var today = document.getElementById("today");
var low = document.getElementById("low");
var high = document.getElementById("high");
var range = document.getElementById("range");
var current = document.getElementById("current");
//設定具體資訊的值
high.innerHTML = stock.high;
range.innerHTML = stock.range;
current.innerHTML = stock.current;
yesterday.innerHTML = stock.yesterday;
today.innerHTML = stock.today;
low.innerHTML = stock.low;
//如果數值比昨天開盤價低,反則就是紅色
if (stock.today > stock.yesterday) {
today.style.color = 'red';
} else {
today.style.color = 'green';
}
if (stock.low > stock.yesterday) {
low.style.color = 'red';
} else {
low.style.color = 'green';
}
if (stock.high > stock.yesterday) {
high.style.color = 'red';
} else {
high.style.color = 'green';
}
//如果現在的價格比昨天開盤高,那麼漲幅是紅色
if (stock.current > stock.yesterday) {
range.style.color = 'red';
current.style.color = 'red';
} else {
range.style.color = 'green';
current.style.color = 'green';
}
}
複製程式碼
只有滑鼠移到超連結上,才明確id的值是多少!
function callBackFunction() {
if (httpRequest.readyState == 4) {
if (httpRequest.status == 200) {
//得到伺服器端返回的JSON資料
json= eval(httpRequest.responseText);
//更新詳細框的資料,當滑鼠移動到超連結上才確定有id,於是判斷有沒有id
if(sid) {
update();
}
//遍歷出每個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';
}
}
}
}
}
function showTool(node) {
//得到滑鼠移動到具體股票的id
sid = node.parentNode.getElementsByTagName("span")[0].id;
//把詳細框框顯示出來
document.getElementById("toolTip").style.display = 'block';
}
function clearTool() {
document.getElementById("toolTip").style.display = 'none';
}
複製程式碼
最終效果:
總結要點
①:這是由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和具體股票物件應該是全域性的變數【這樣才能夠在別的方法中用到】
驗證碼校驗
對於驗證碼檢查我們並不會陌生,我們在學習Session的時候已經使用過了驗證碼檢查了。詳細可參考:blog.csdn.net/hon_3y/arti…
我們當時是同步檢查驗證碼是否正確的,其實沒有必要。因為就驗證一個輸入框的資料,沒必要使用同步的方式驗證【使用非同步對使用者體驗更加友好】
分析
當使用者輸入完4位數字的時候,就去伺服器端驗證是否需要相同,如果相同,那麼返回一個打鉤的圖片。如果不同,那麼就返回一個打叉的圖片
前臺分析
- 繫結鍵盤輸入事件
- 當輸入數達到4的時候,就與伺服器互動
- 得到伺服器帶過來的圖片,使用DOM新增到對應的位置
後臺分析
- 得到前臺帶過來的值
- 判斷該值與Session儲存的是否相同
- 根據判斷值返回對應的圖片
編寫JSP
值得注意的是:要獲取td定義的id,外邊一定要套上table標籤。。。我在剛開始寫的時候,是沒有table標籤的。然後死活得不到td的標籤....很煩...
<%--
Created by IntelliJ IDEA.
User: ozc
Date: 2017/5/17
Time: 20:52
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>驗證碼校驗</title>
<script type="text/javascript" src="js/ajax.js"></script>
</head>
<body>
<%--###################展示頁面#############################--%>
<table>
<tr>
<td>驗證碼:</td>
<td><input type="text" id="checkCode" name="checkCode"></td>
<td><img src="01_image.jsp"/></td>
<td id="result"></td>
</tr>
</table>
<%--###################去除空格方法#############################--%>
<script type="text/javascript">
function trim(str) {
//去除左邊的空格
str.replace("/^\s*/", "");
//去除右邊的空格
str.replace("/\s*$/", "");
return str;
}
</script>
<%--###################繫結鍵盤事件#############################--%>
<script type="text/javascript">
document.getElementById("checkCode").onkeyup = function () {
//得到輸入框的內容,把的前後空格都去除
var keyValue = this.value;
keyValue = trim(keyValue);
/*******************ajax程式碼*******************************/
if (keyValue.length == 4) {
var ajax = createAJAX();
var method = "post";
var url = "${pageContext.request.contextPath}/CheckCodeServlet?time=" + new Date().getTime();
ajax.open(method, url);
ajax.setRequestHeader("content-type", "application/x-www-form-urlencoded");
ajax.send("keyValue=" + keyValue);
/*******************ajax回撥函式*******************************/
ajax.onreadystatechange = function () {
if (ajax.readyState == 4) {
if (ajax.status == 200) {
//得到伺服器帶過來的資料
var tip = ajax.responseText;
/*******************使用DOM把資料新增到頁面上*******************************/
var img = document.createElement("img");
img.src = tip;
img.style.width = "14px";
img.style.height = "14px";
var td = document.getElementById("result");
td.innerHTML = "";
td.appendChild(img);
}
}
};
}else {
//清空圖片
var td = document.getElementById("result");
td.innerHTML = "";
}
};
</script>
</body>
</html>
複製程式碼
- 處理請求的Servlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by ozc on 2017/5/17.
*/
@WebServlet(name = "CheckCodeServlet",urlPatterns = "/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到帶過來的資料
String keyValue = request.getParameter("keyValue");
//得到Session中的資料
String checkCodeInSession = (String) request.getSession().getAttribute("CHECKNUM");
response.setContentType("text/html;charset=UTF-8");
String src = "images/MsgError.gif";
//判斷倆資料是否相同
if (keyValue.equals(checkCodeInSession)) {
src = "images/MsgSent.gif";
}
PrintWriter writer = response.getWriter();
writer.write(src);
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
複製程式碼
測試
總結
- 使用AJAX驗證校驗碼主要是監聽鍵盤的響應事件
- 要獲取td標籤的資料,外邊一定要套有table標籤!【別偷懶不寫table標籤】
- 當輸入框的數值數為4的時候就與伺服器進行互動,伺服器返回一張圖片。
- 可以用自定義的trim()把資料的前後空格去掉,通過正規表示式來去除空格。
- 當輸入框的數值數不為4的時候就把圖片的內容清空
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y