效果圖:

代碼:
<body>
<script type="text/javascript">
window.onload = newgame; //頁面載入的時候就開始一個新的游戲
window.onpopstate = popState; //處理歷史記錄相關事件
var state,ui; //全局變量,在newgame()方法中會對其初始化
function newgame( playagin ){ //開始一個新的猜數字游戲
//初始化一個包含需要的文檔元素的對象
ui ={
heading: null, //文檔最上面的<h1>元素
prompt : null, //要求用戶輸入一個猜測數字
input : null, //用戶輸入猜測數字的地方
low : null, //可視化的三個表單單元格
mid : null, //猜測的數字范圍
high : null
};
//查詢這些元素中每個元素的id
for(var id in ui)
ui[id] = document.getElementById(id);
//給input字段定義一個事件處理程序函數
ui.input.onchange = handleGuess;
//生成一個隨機的數字並初始化游戲狀態
state ={
n : Math.floor(99*Math.random())+1, //整數: 0<n<100
low : 0, //可猜測數字范圍的下限
high : 100, //可猜測數字范圍的上限
guessnum : 0 , //猜測次數
guess : undefined //最后一次猜測
};
//修改文檔內容來顯示該初始狀態
display(state);
/**
* 此函數會作為onload事件處理程序調用,
* 同時當單機顯示在游戲最后的“再玩一次”按鈕時候,也會調用它
* 在第二種調用情況下,playagain參數值為true
* 如果playagain為true,則保存新的游戲狀態
* 但是如果是作為onload事件處理程序調用的情況下,則不保存狀態
* 這是因為,當通過瀏覽器歷史記錄從其他文檔狀態回退到當前的游戲狀態時,
* 也會出發load事件,如果這種情況下,也保存狀態的話
* 會將真正的游戲歷史狀態記錄覆蓋掉
* 在支持pushState()方法的瀏覽器中,load事件之后總是有一個popstate事件
* 因此,這里的處理方式是,等待popstate事件而不是之間進行狀態保存
* 如果該事件提供一個狀態對象,則直接使用該對象即可
* 如果該事件沒有狀態對象,就表示這實際上是一個新的游戲
* 則使用replaceState來保存最新的游戲狀態
*/
if(playagin ==true)
save(state);
}
//如果支持的話,就使用pushState()方法將游戲狀態保存到瀏覽器歷史記錄中
function save(state){
if(!history.pushState)
return; //如果pushState()方法沒有定義的話,則什么也不做
//這里會將一個保存的狀態和URL關聯起來
//該URL顯示猜測的數字,但是不對游戲狀態進行編碼
//因此,這對於書簽是沒有用的
//不能簡單地游戲狀態寫到URL中,因為這會將游戲一些機密數字暴露在地址欄中
var url = "#guess"+state.guessnum;
//保存狀態對象和URL
history.pushState(state, //要保存的狀態對象
"", //狀態標題,當前瀏覽器會忽略它
url //狀態URL:對書簽是沒有用的
);
}
//這是onpopstate的事件處理程序,用於恢復歷史狀態
function popState(event){
if(event.state){ //如果事件有一個狀態對象,則恢復該狀態
//要注意的是,event.state是對已保存狀態對象的一個深拷貝
//因此無須改變保存的值就可以修改該對象
state = event.state; //恢復歷史狀態
display(state); //顯示恢復的狀態
}else{
//當第一次載入頁面時,會觸發一個沒有狀態的popstate事件
//用真實的狀態將null狀態替換掉: 參見newgame()方法中的注釋
//這里不需要調用display()方法
history.replaceState(state,"","#guess"+state.guessnum);
}
};
//每次用戶猜測一個數字的時候,都會調用此事件處理程序
//此處處理程序用於更新游戲的狀態、保存游戲狀態並顯示游戲游戲狀態
function handleGuess(){
//從input字段中獲取用戶猜測的數字
var g = parseInt(this.value);
//如果該值是限定范圍中的一個數字
if((g > state.low)&&(g<state.high)){
//對應的更新狀態對象
if(g < state.n)
state.low = g;
else
if(g > state.n)
state.high = g;
state.guess = g;
state.guessnum++;
//在瀏覽器歷史記錄中保存新的狀態
save(state);
//根據用戶猜測情況來修改文檔
display(state);
}
else{ //無效的猜測
alert("請輸入一個比"+state.low+",並且比"+state.high+"大的數。");
}
}
//修改文檔顯示游戲當前狀態
function display(state){
//顯示文檔的導航和標題
ui.heading.innerHTML =document.title="我認為這個數字是"+state.low+"~"+state.high+"之間。";
//使用一個表格來顯示數字的取值范圍
ui.low.style.width = state.low+"%";
ui.mid.style.width = (state.high-state.low)+"%";
ui.high.style.width = (100-state.high)+"%";
//根據用戶最近的猜測,設置提示
if(state.guess===undefined)
ui.prompt.innerHTML = "你的輸入有誤!";
else if(state.guess < state.n)
ui.prompt.innerHTML = state.guess + "太小了,請重新輸入:";
else if(state.guess > state.n)
ui.prompt.innerHTML = state.guess + "太大了,請重新輸入:";
else{
//當才對了的時候,就隱藏input字段並顯示“再玩一次”按鈕
ui.input.style.visibility = "hidden"; //不需要再猜了
ui.heading.innerHTML = document.title = state.guess+"命中答案。"
ui.prompt.innerHTML = "你贏了,<button onclick='newgame(true)'>再玩一次</button>";
}
}
</script>
<style type="text/css">
#prompt {font-size: 16pt;}
table{width: 90%; margin: 10px; margin-left: 5%;}
#low,#high{ background: lightgray; height: 1em; }
#mid{background-color: green;}
</style>
<h1 id="heading">我在等一個數:</h1>
<table>
<tr>
<td id="low"></td>
<td id="mid"></td>
<td id="high"></td>
</tr>
</table>
<label id="prompt"></label><input type="text" id="input">
</body>
