之前简单地介绍过JavaScript的一些基础知识,包括循环、函数、对象等,这里将通过一个bingo游戏的例子介绍对它们的应用。
一, 用循环进行重复操作
这个示例演示如何建立和使用循环,用随机生成的数字填充 Bingo 卡片的内容:
test.html文件:
<!DOCTYPE html>
<html>
<head>
<title>Make Your Own Bingo Card</title>
<link rel="stylesheet" href="test.css">
<script src="test.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"> </td>
<td id="square5"> </td>
<td id="square10"> </td>
<td id="square14"> </td>
<td id="square19"> </td>
</tr>
<tr>
<td id="square1"> </td>
<td id="square6"> </td>
<td id="square11"> </td>
<td id="square15"> </td>
<td id="square20"> </td>
</tr>
<tr>
<td id="square2"> </td>
<td id="square7"> </td>
<td id="free">Free</td>
<td id="square16"> </td>
<td id="square21"> </td>
</tr>
<tr>
<td id="square3"> </td>
<td id="square8"> </td>
<td id="square12"> </td>
<td id="square17"> </td>
<td id="square22"> </td>
</tr>
<tr>
<td id="square4"> </td>
<td id="square9"> </td>
<td id="square13"> </td>
<td id="square18"> </td>
<td id="square23"> </td>
</tr>
</table>
<p><a href="script01.html" id="reload">Click here</a> to create a new card</p>
</body>
</html>
test.css文件:
body {
background-color: white;
color: black;
font-size: 20px;
font-family: "Lucida Grande", 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(images/redFlash.gif);
}
test.js文件:
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;
}
}
- 使用JavaScript内置的 Math.random()生成 0~ 1 的一个随机数。
- 将随机数简单的数学处理后,依据id顺序填入空格。
二,将值传递给函数
将上面的数字填充语句用接收参数的函数来实现:
window.onload = initAll;
function initAll() {
for (var i=0; i<24; i++) {
setSquare(i);
}
}
function setSquare(thisSquare) {
var currSquare = "square" + thisSquare;
var newNum = Math.floor(Math.random() * 75) + 1;
document.getElementById(currSquare).innerHTML = newNum;
}
- setSquare()函数接受一个参数作为所依据的id,然后填入数据。
三,探测对象
在编写脚本时,你可能希望检查浏览器是否有能力理解你要使用的对象。进行这种检查的方法称为对象探测( object detection)。方法是对要寻找的对象进行条件测试,如 if (对象) {…} ,如果对象存在,if 语句就为 true,脚本继续执行。
在调用数字填充语句前添加对象探测:
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 newNum = Math.floor(Math.random() * 75) + 1;
document.getElementById(currSquare).innerHTML = newNum;
}
- if (document.getElementById)在JavaScript操作对象存时才执行对应的操作。
如果脚本使用的对象并没有得到浏览器 100%的支持,那么总是应该首先检查浏览器是否能够处理它,而不要想当然地认为浏览器可以处理它。
四,处理数组
在真实的 Bingo 卡片上,每列具有不同的数字范围: B 列是 1~ 15, I 列是 16~ 30, N 列是 31~ 45, G 列是 46~ 60, O 列是 61~ 75。而我们之前JavaScript填充的内容不一定是符合规则的。
了解数字规则之后,使用数组对象重新填充正确数字:
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;
}
- 新建数组用于保存一个基数,方便后续数字处理。
五,处理有返回值的函数
将上面产生随机数的语句用有返回值的函数代替:
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 + getNewNum() + 1;
document.getElementById(currSquare).innerHTML = newNum;
}
function getNewNum() {
return Math.floor(Math.random() * 15);
}
六,更新数组
目前为止,这个 Bingo 卡片脚本还无法确保给定的列中不出现重复的数字。
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 = colBasis + getNewNum() + 1;
if (!usedNums[newNum]) {
usedNums[newNum] = true;
document.getElementById(currSquare).innerHTML = newNum;
}
}
function getNewNum() {
return Math.floor(Math.random() * 15);
}
- 这里数组对[1,75]这个区间的共76数字进行标记。如果不对布尔值进行初始化,那么它们会自动设置为 false。
- 如果 usedNums 数组中的 newNum 位置上是 false,那么就将它设置为
true,并将 newNum 写到卡片上第currSquare个空格中。如果 newNum 位置上是 true,就什么也不做。这样就不会有重复的数字。
七,使用 do/while 循环
上面的脚本虽然能通过对已用的数字进行标记从而确保给定的列中不出现重复的数字,但会导致某些地方出现空白,因为经过colBasis + getNewNum() + 1处理,可能这个数字因为被用过而被记为true,从而导致所需填充数字的空格里什么都没有。
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] = true;
document.getElementById(currSquare).innerHTML = newNum;
}
function getNewNum() {
return Math.floor(Math.random() * 15);
}
- 这个 do/while 循环将跳过被用过的数字,直到找到第一个该列中所允许但又未被使用过的数字进行填充。
do/while
循环的一种常见用途是,从用户输入的数据中去掉空格或无效的字符。
八,以多种方式调用脚本
到目前为止,脚本都是在加载页面时自动运行的。但是在现实环境中,常常希望让用户对脚本有更多的控制能力,甚至允许他们控制脚本在何时运行。
在下面这个示例中,脚本仍然在加载页面时运行。但是,还允许用户单击页面底部的链接来重新运行脚本,这样就可以完全在浏览器中生成 Bingo 卡片,而不需要从服务器重新加载页面。这向用户提供了快速的响应,而且不会产生服务器负载。
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=1; i<usedNums.length; i++) {
usedNums[i] = false;
}
newCard();
return false;
}
- 页面加载完成后,在 id 为 reload 的标签上设置链接,让它在被单击时调用 anotherCard()。 initAll()函数中原来的所有计算现在被转移到新的 newCard()函数中。
- 在 anotherCard()函数执行 3 个操作:1,将 usedNums[]数组中的所有元素设置为 false(这样就可以重新使用所有数字);2,调用 newCard()函数(生成另一个卡片);3,返回 false 值,使浏览器不尝试加载链接的 href 中指定的页面。
这种使用 JavaScript 重新加载页面的一部分而不需要向服务器请求整个新页面的情况,而这正是 Ajax 基本特色。
九,组合使用 JavaScript 和 CSS
目前,看不到使用JavaScript进行的任何用户交互,接下来让用户能够操作所生成的 Bingo 卡片——点击某个数字,其背景变色。
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=1; 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 = "";
}
}
- onmousedown事件触发时调用toggleColor()函数。
- toggleColor()函数中第一个if…else语句分别对应 IE 方式和其他所有浏览器处理事件的方式。
- toggleColor()函数中第二个if…else语句检查被单击的格子的 class 属性是否有一个值。如果没有,就给它设置 pickedBG 值,格子变色表示这个数字已经被选择了。
十,检查状态
让用户能够给格子加标记之后,还应该检查格子是否构成了获胜模式。
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=1; 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();
}
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;
}
}
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";
}
}
}
}
- winningOption存储用户可能遇到的获胜选项;setSquares存储已经单击的格子;winners是一个数字数组,其中的每个数字是有效获胜组合的编码值。
- checkWin()函数的第一个for需要检查它的号码是否已经被叫过。如果被叫到,对setSquares 和 Math.pow(2,i)执行按位“或”操作会产生一个表示用户所处状态的变量。
- checkWin()函数的第二个for,把卡片的当前状态与每个获胜状态作比较。
- 如果 winningOption 是大于-1 的数字,就说明用户是获胜者。在这种情况下,checkWin()函数的第三个for遍历每个格子,检查在获胜模式中是否可以找到它。如果是,就把 class 属性设置为 winningBG。
到这里,就完成这个bingo游戏的编写了。