了解 JavaScript (4)– 第一个 Web 应用程序

在下面的例子中,我们将要构建一个 Bingo 卡片游戏,每个示例演示 JavaScript 的不同方面,通过每次的改进将会得到最终有效的 Bingo 卡片。

Bingo 卡片的内容

美国 Bingo 卡片是 5 X 5 的方形,5 个列上标明 B-I-N-G-O,格子中包含 1~75 的数字。正中间是一个空格子,常常印着 free。每列可以包含的范围如下:

  • B 列包含数字 1~15
  • I 列包含数字 16~30
  • N 列包含数字 31~45
  • G 列包含数字 46~60
  • O 列包含数字 61~75

原始1、第一个简单的 JavaScript 循环

bingo.html,这个页面建立 Bingo 卡片的框架。

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Make Your Own Bingo</title>
  <link rel="stylesheet" href="bingo.css">
  <script src="bingo.js"></script>
</head>
<body>
<h1>Create A Bingo Card</h1>
<table>
<tr>
  <th>B</th>
  <th>I</th>
  <th>N</th>
  <th>G</th>
  <th>O</th>  
</tr>

<tr>
  <td id="square0">&nbsp;</td>
  <td id="square5">&nbsp;</td>
  <td id="square10">&nbsp;</td>
  <td id="square14">&nbsp;</td>
  <td id="square19">&nbsp;</td>  
</tr>

<tr>
  <td id="square1">&nbsp;</td>
  <td id="square6">&nbsp;</td>
  <td id="square11">&nbsp;</td>
  <td id="square15">&nbsp;</td>
  <td id="square20">&nbsp;</td>  
</tr>

<tr>
  <td id="square2">&nbsp;</td>
  <td id="square7">&nbsp;</td>
  <td id="free">Free</td>
  <td id="square16">&nbsp;</td>
  <td id="square21">&nbsp;</td>  
</tr>

<tr>
  <td id="square3">&nbsp;</td>
  <td id="square8">&nbsp;</td>
  <td id="square12">&nbsp;</td>
  <td id="square17">&nbsp;</td>
  <td id="square22">&nbsp;</td>  
</tr>

<tr>
  <td id="square4">&nbsp;</td>
  <td id="square9">&nbsp;</td>
  <td id="square13">&nbsp;</td>
  <td id="square18">&nbsp;</td>
  <td id="square23">&nbsp;</td>  
</tr>
</table>
<p><a href="../111.html" id="reload">Click here</a> to create a new card</p>
</body>
</html>

bingo.css 为 Bingo 卡片添加样式

@charset "utf-8";
/* CSS Document */
body{
    background-color: white;
    color: black;
    font-size: 20px;
    font-family: "Lucida Console", Verdana, Arial, Helvetica, sans-serif;
}

h1, th{
    font-family: Georgia, "Times New Roman", Times, serif;
}

h1 {
    font-size: 28px;
}

table{
    border-collapse: collapse;
}
th, td{
    padding: 10px;
    border: 2px #666 solid;
    text-align: center;
    width: 20%;
}

#free, .pickedBG{
    background-color: #f66;
}
.winningBG{
    background-image: url(redFlash.gif);
}

我们先看看上面的 HTML 和 CSS 页面,这个是 Bingo 卡片的框架,包含 6 行 5 列的表格,表格的第一行是BINGO字母,且第三行中间是一个 Free 表格,每个单元格都有一个 id 属性,脚本将使用此 id 操纵这些单元格内容。id 采用 square0、square1、square2,直到 square23 分布如下图,在页面的底部,有一个用来生成新卡片的链接。

20150901001

我们将要用循环语句,最常用的就是 for 循环,这种循环使用一个计数器(counter),计数器是一个变量,它的初值是某个值。在测试条件得不到满足的时候就会结束。

我们将 bingo.html<head> 标签中的 <script> src 属性设置为 bingo01.js

bingo01.js,使用 for 循环将表格内容填写为 1~75 中数,如下:

// JavaScript Document
window.onload = initAll;

function initAll(){
  for(var i=0; i<24; i++){
      var newNum = Math.floor(Math.random() * 75) + 1;
      document.getElementById('square' + i).innerHTML = newNum;
  }
}

Math.random() 生成一个 0~1 的随机数,比如 0.123456789。然后与最大值(75)相乘,就会生成 0~75 之间的结果。对结果进行 Math.floor() 操作获得结果的整数部分。

该 bingo01.js 的显示结果如下:

20150901002

第一个示例产生的卡片还不是有效的 Bingo 卡片,因为 Bingo 卡片对特定列有一些要求。后面的示例将对脚本进行改进,直到产生有效的 Bingo 卡片。


改进2、将值传递给函数

我们继续改进 bingo01.js,并保存为 bingo02.js,如下:

// JavaScript Document
window.onload = initAll;

function initAll(){
  for(var i=0; i<24; i++){
    //我们将i值传递给 setSquare() 函数
    setSquare(i);  
  }
}

function setSquare(thisSquare){
  var currSquare = "square" + thisSquare;
  var newNum = Math.floor(Math.random() * 75) + 1;
  document.getElementById(currSquare).innerHTML = newNum;    
}

通过将值传递给 setSquare() 函数,使得脚本更加容易阅读和理解了。


改进3、探测对象

在编写脚本时,你可能希望检查浏览器是否有能力理解你要使用的对象。进行这种检查的方法称为对象探测(object detection)。如下:

if (document.getElementById){

如果对象存在,if 语句就为 true,脚本继续执行。但是如果浏览器不理解这个对象,就会返回 false,并执行条件语句 else 部分。

bingo03.js,增加了对象探测功能,如下:

// JavaScript Document
window.onload = initAll;

function initAll(){
  if(document.getElementById){
    //如果 document.getElementById 这个对象存在的话    
    for(var i=0; i<24; i++){
      setSquare(i);  
    }
  }else{
    alert('Sorry, your browser doesn\'t support this script!');  
  }
}

function setSquare(thisSquare){
  var currSquare = "square" + thisSquare;
  var newNum = Math.floor(Math.random() * 75) + 1;
  document.getElementById(currSquare).innerHTML = newNum;    
}

改进4、处理数组

在这个示例中,我们将使用一个有用的 JavaScript 对象,数组(array)。

数组是一种可以存储一组信息的变量,与变量一眼个,可以使包含任何类型的数据。声明数组时,将数组元素放在括号中,以逗号分隔,如下:

var newCars = new Array("Toyota", "Honda", "Nissan");

在这个示例中,我们要确保 Bingo 卡片是仍然不是有效的,每列虽然具有不同的数字范围,但是一些列中出现重复的数字。

bingo04.js,这个脚本限制了每一列值的范围。

window.onload = initAll;

function initAll(){
  if(document.getElementById){
    for(var i=0; i<24; i++){
      setSquare(i);
    }
  }else{
    alert('Sorry, your browser doesn\'t support this script');  
  }
}

function setSquare(thisSquare){
  var currSquare = 'square' + thisSquare;
  var colPlace = new Array(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4);
  var colBasis = colPlace[thisSquare] * 15;
  var newNum = colBasis + Math.floor(Math.random() * 15) + 1;
  
  document.getElementById(currSquare).innerHTML = newNum;    
}

该脚本执行后,显示如下:

20150905001


改进5、处理有返回值的函数、运行时更新数组

我们将一些计算转移到一个函数中,这个函数为 Bingo 卡片上的单元格返回随机数,然后另一个函数使用它返回的结果,这使得整个更加容易理解了。

我们还要纠正一个问题, 避免出现重复的数字,在脚本运行时通过计算修改数组中的值。

bingo05.js,增加了一个 getNewNum 函数,返回一个 0~14 之间的数,新增了一个 usedNums 数组,存放 true / false ,主要是避免重复的数字。

// JavaScript Document
window.onload = initAll;
var usedNums = new Array(76); //已经初始化为 false

function initAll(){
  if(document.getElementById){
    for(var i=0; i<24; i++){
      setSquare(i);
    }
  }else{
    alert("Sorry, your browser doesn't support this script");
  }
}

function setSquare(thisSquare){
  var currSquare = 'square' + thisSquare;
  var colPlace = new Array(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4);
  var colBasis = colPlace[thisSquare] * 15;
  var newNum = colBasis + getNewNum() + 1;
  //如果 usedNums[newNum] 为 true ,说明有重复数据,不做任何处理
  if(!usedNums[newNum]){
    usedNums[newNum] = true;
    document.getElementById(currSquare).innerHTML = newNum;
  }
    
}

function getNewNum(){
  return Math.floor(Math.random() * 15);    
}

脚本执行后,显示如下:

20150905002


改进6、使用 do/while 循环

有时候,需要让代码循环许多次,但是无法确定需要循环多少次。在这种情况下,就要使用 do/while 循环,只要某个条件为 true, 就执行某种操作。

bingo06.js 脚本,在将数字放入单元格之前,首先检查是否已经使用了这个数字。如果这个数字已经使用过了,脚本就将生成新的随机数并重复该过程,直到找到不重复的数字为止,该脚本最终生成了有效的 Bingo 卡片。

// JavaScript Document
window.onload = initAll;
var usedNums = new Array(76);

function initAll(){
  if(document.getElementById){
    for(var i=0; i<24; i++){
      setSquare(i);
    }
  }else{
    alert('Sorry, your browser doesn\'t support this script');
  }
}

function setSquare(thisSquare){
  var currSquare = 'square' + thisSquare;
  var colPlace = new Array(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4);
  var colBasis = colPlace[thisSquare] * 15;
  var newNum;
    
  do{
    newNum = colBasis + getNewNum() + 1;
  }while (usedNums[newNum]); 
  //检查 usedNums[] 数组中 newNum 上的值,
  //从而判断 newNum 是否已经使用过了
    
  document.getElementById(currSquare).innerHTML = newNum;
}

脚本执行后,我们得到了有效的 Bingo 卡片,如下:

20150905003


改进7、多种方式调用脚本

到目前为止,我们看到的脚本都是在加载页面的时候自动运行的。但是在显示环境中,常常希望用户对脚本有更多的控制能力,甚至允许他们控制脚本在何时运行。

bingo07.js 脚本中,允许我们通过页面底部的链接来重新运行脚本,这样就可以完全在浏览器中生成 Bingo 卡片,而不需要从服务器重新加载页面。这向用户提供了快速的响应,而且不会产生服务器负载。

// JavaScript Document
window.onload = initAll;
var usedNums = new Array(76);

function initAll(){
  if(document.getElementById){
    document.getElementById("reload").onclick = anotherCard;
    newCard();
  }else{
    alert("Sorry, your browser doesn't support this script");
  }
}

function newCard(){
  for(var i=0; i<24; i++){
    setSquare(i);
  }
}

function setSquare(thisSquare){
  var currSquare = 'square' + thisSquare;
  var colPlace = new Array(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4);
  var colBasis = colPlace[thisSquare] * 15;
  var newNum;
    
  do{
    newNum = colBasis + getNewNum() + 1;
  }while(usedNums[newNum])
    
  usedNums[newNum] = true;
  document.getElementById(currSquare).innerHTML = newNum;
}

function getNewNum(){
  return Math.floor(Math.random() * 15);
}

function anotherCard(){
  for(var i=0; i<usedNums.length; i++){
    usedNums[i] = false;
  }
  newCard();
  return false;
  //是浏览器不加载 href 中的链接地址
}

其实到了现在,你已经知道如何使用 JavaScript 进行重新加载一部分的页面了,而不需要向服务器请求整个页面,这正是 Ajax 的基本特色。


改进8、组合使用 JavaScript 和 CSS

该示例中我们将演示如何让用户能够操作所生成的 Bingo 卡片。

bingo08.js 脚本中我们将添加一些 JavaScript 来利用 CSS 的功能。

// JavaScript Document
window.onload = initAll;
var usedNums = new Array(76);

function initAll(){
  if(document.getElementById){
    document.getElementById("reload").onclick = anotherCard;
    newCard();
  }else{
    alert("Sorry, your browser doesn't support this script");        
  }
}

function newCard(){
  for(var i=0; i<24; i++){
    setSquare(i);
  }
}

function setSquare(thisSquare){
  var currSquare = 'square' + thisSquare;
  var colPlace = new Array(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4);
  var colBasis = colPlace[thisSquare] * 15;
  var newNum;
    
  do{
    newNum = colBasis + getNewNum() + 1;
  }while(usedNums[newNum]);
  usedNums[newNum] = true;
  document.getElementById(currSquare).innerHTML = newNum;
    //确保 Bingo 卡片最初状态都是 class 属性都是空字符串
  document.getElementById(currSquare).className = "";
    //设置 每个 td 的 onmousedown 事件
  document.getElementById(currSquare).onmousedown = toggleColor;
}

function getNewNum(){
  return Math.floor(Math.random() * 15);    
}

function anotherCard(){
  for(var i=0; i<usedNums.length; i++){
    usedNums[i] = false;
  }
  newCard();
  return false;
}

function toggleColor(evt){
  //为了处理 IE 和 非IE 浏览器,如果是 非IE 则会有一个 evt 传递给函数
    if(evt){
    var thisSquare = evt.target;
  }else{
  //如果是 IE 浏览器 则必须通过下面的语句获得
    var thisSquare = window.event.srcElement;
  }
  if(thisSquare.className == ""){
    thisSquare.className = "pickedBG";
  }else{
    thisSquare.className = "";
  }
/*
  //自行通过 this 也可以获取当前元素进行修改,代码如下
  if(this.className == ""){
    this.className = "pickedBG";
  }else{
    this.className = "";
  }
  alert(this.className);
*/
}

脚本执行后,可以通过鼠标点击单元格,点击后的单元格背景变成红色,如下:

20150905004


改进9、检查状态

用户能够给格子添加标记后,还可以检查格子是否构成了获胜模式。在下面的脚本中,用户选择那些已经被叫过的号码,bingo09.js 脚本会让用户知道什么时候他们获胜了,该脚本使用了复杂的数学计算来判断获胜组合。

// JavaScript Document
window.onload = initAll;
var usedNums = new Array(76);

function initAll(){
  if(document.getElementById){
    document.getElementById("reload").onclick = anotherCard;
    newCard();
  }else{
    alert("Sorry, your browser doesn't support this script");
  }
}

function newCard(){
  for(var i=0; i<24; i++){
    setSquare(i);
  }
}

function setSquare(thisSquare){
  var currSquare = 'square' + thisSquare;
  var colPlace = new Array(0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4);
  var colBasis = colPlace[thisSquare] * 15;
  var newNum;
  do{
    newNum = colBasis + getNewNum() + 1;
  }while(usedNums[newNum]);
  usedNums[newNum] = true;
  document.getElementById(currSquare).innerHTML = newNum;
  document.getElementById(currSquare).className = "";
  document.getElementById(currSquare).onmousedown = toggleColor;
}

function getNewNum(){
  return Math.floor(Math.random() * 15);
}

function anotherCard(){
  for(var i=0; i<usedNums.length; i++){
    usedNums[i] = false;
  }
  newCard();
  return false;
}

function toggleColor(evt){
  if(evt){
    var thisSquare = evt.target;
  }else{
    var thisSquare = window.event.srcElement;
  }
  if(thisSquare.className == ""){
    thisSquare.className = "pickedBG";
  }else{
    thisSquare.className = "";
  }
  //此处增加 checkWin 函数,随时检查是否获胜
  checkWin();
}

function checkWin(){
//存储用户的获胜选项 
  var winningOption = -1;
//存储已经点击的格子
  var setSquares = 0;
//存储获胜组合的数组
  var winners = new Array(31, 992, 15360, 507904,  541729, 557328, 1083458, 2162820, 4329736, 8519745, 8659472, 16252928);
//检查每一个格子是否点击过  
  for(var i=0; i<24; i++){
    var currSquare = "square" + i;
    if(document.getElementById(currSquare).className != ""){
      document.getElementById(currSquare).className = "pickedBG";
      //按位运算,或 操作,记录用户点击的组合
      setSquares = setSquares | Math.pow(2, i);    
    }
  }
  //与 操作,判断是否获胜
  for(var i=0; i<winners.length; i++){
    if((winners[i] & setSquares) == winners[i]){
      winningOption = i;
    //i就是用户获胜的状态
    }
  }
  //如果是获胜者,则遍历每个格子,判断改格子是否获胜的格子
  if(winningOption > -1){
    for(var i=0; i<24; i++){
      if(winners[winningOption] & Math.pow(2, i)){
        currSquare = "square" + i;
        document.getElementById(currSquare).className = "winningBG";
        //winningBG 将设置背景为 GIF 动画
      }
    }
  }
}

脚本执行后, 我们尝试选择对角线上格子,背景图案闪烁,说明获胜了,如下图:

20150905005


改进10、处理字符串数组

到目前为止,我们所使用的数组都是由布尔值或者数字组成的,bingo10.js 脚本将演示使用字符串数组综合前面的所有技术,创建一个 Buzzword Bingo 游戏。

//buzzwords 数组至少需要24个元素
var buzzwords = new Array("Aggregate", "Ajax", "API", "Bandwidth", "Beta", "Bleeding edge", "Convergence", "Design pattern", "Disruptive", "DRM",  "Enterprise",  "Facilitate", "Folksonomy", "Framework", "Impact", "Innovate", "Long tail", "Mashup", "Microformats", "Mobile", "Monetize", "Open social", "Paradigm", "Podcast", "Proactive", "Rails", "Scalable", "Social bookmarks", "Social graph", "Social software", "Spam", "Synergy", "Tagging", "Tipping point", "Truthiness", "User-generated", "Vlog", "Webinar", "Wiki", "Workflow");
var usedWords = new Array(buzzwords.length);
window.onload = initAll;

function initAll(){
  if(document.getElementById){
    document.getElementById("reload").onclick = anotherCard;
    newCard();
  }else{
    alert("Sorry, your browser doesn't support this script");
  }
}

function newCard(){
  for(var i=0; i<24; i++){
    setSquare(i);
  }
}

function setSquare(thisSquare){
  do{
    var randomWord = Math.floor(Math.random() * buzzwords.length);
  }
  while(usedWords[randomWord]);
  usedWords[randomWord] = true;
  var currSquare = 'square' + thisSquare;
  document.getElementById(currSquare).innerHTML = buzzwords[randomWord];
  document.getElementById(currSquare).className = "";
  document.getElementById(currSquare).onmousedown = toggleColor;
}

function anotherCard(){
  for(var i=0; i<buzzwords.length; i++){
    usedWords[i] = false;
  }
  newCard();
  return false;
}

function toggleColor(evt){
  if(evt){
    var thisSquare = evt.target;
  }else{
    var thisSquare = window.event.srcElement;
  }
  if(thisSquare.className == ""){
    thisSquare.className = "pickedBG";
  }else{
    thisSquare.className = "";
  }
  checkWin();
}

function checkWin(){
  var winningOption = -1;
  var setSquares = 0;
  var winners = new Array(31, 992, 15360, 507904, 557328, 1083458, 2162820, 4329736, 8519745, 8659472, 16252928);
 
  for(var i=0; i<24; i++){
    var currSquare = 'square' + i;
    if(document.getElementById(currSquare).className != ''){
      document.getElementById(currSquare).className = 'pickedBG';
      setSquare = setSquare | Math.pow(2, i);
    }
  }
 
  for(var i=0; i<winners.length; i++){
    if((winners[i] & setSquare) == winners[i]){
      winningOption = i;
    }
  }
 
  if(winningOption > -1){
    for(var i=0; i<24; i++){
      if(winners[winningOption] & Math.pow(2, i)){
        currSquare = 'square' + i;
        document.getElementById(currSquare).className = "winningBG";
      }
    }
  }
}

脚本执行后效果如下:

20150906001


示例代码下载:

Bingo.rar

posted on 2015-09-06 10:38  pchmonster  阅读(3016)  评论(1编辑  收藏  举报

导航