效果圖:
代碼:
<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>