DOM练习小记--简单的拼单词游戏

(资料源自《Head First Ajax》第七章)

书中另一个单页游戏案例,综合了DOM和事件处理,先总结一下页面布局和js编程的思路。涉及到服务端交互的暂时按下,毕竟还没有自己把服务端环境搭起来。

1) 内容简介

书中给的样本页面和样式是布置好的,初始静态页面如下:

  1. 4row*4col布局,每次初始化都会随机出现16个字母图片;
  2. 点击任意字母会把对应字母输入到旁边字母框中,每次提交前每个字母只能点击一次;
  3. 需要拼出一个合法的单词,OK后点击submit提交(Ajax);
  4. 如果合法则在分数框计分数(元音+2,辅音+1),并把该单词输出在下面的单词框中;如果不合法则提示单词错误;
  5. 输入框和字母盘刷新;

HTML/CSS

与拼图游戏的表格布局不同,这一次的布局使用了“<div>+CSS”,相比表格布局,CSS布局的优点是更灵活,更保持在所有浏览器中的一致性,并有利于优化和维护;

  <div id="letterbox">
   <a href="#" class="tile t11"></a>
   <a href="#" class="tile t12"></a>
   <a href="#" class="tile t13"></a>
   <a href="#" class="tile t14"></a>
   ……

字母盘大概的布局像这样重复的4列,通过class的通用类名和特殊类名能方便地设置样式和定位;

对于每个方块中的字母图片也是在CSS中设置的背景:

#letterbox a.tile { background: url(‘../images/tiles.png‘) ……}
#letterbox a.la { background-position: 0px 0px; }
#letterbox a.lb { background-position: -80px 0px; }
#letterbox a.lc { background-position: -160px 0px; }
#letterbox a.ld { background-position: -240px 0px; }
#letterbox a.le { background-position: -320px 0px; }
#letterbox a.lf { background-position: -400px 0px; }
……

实际上为所有方块中的背景加载的背景是同一张图,只是通过为不同字母标志的类名设置不同的显示位置,就能显示对应的字母图像,而不必每次都为每个方块去加载单个的图像。

旁边的输入框和单词收集框都是div,有各自id,在操作DOM增删节点时非常方便。

2) 总体思路

  1. 通用方法:除需要添加window.onload事件的addLoadEvent和创建请求的createReqest之外,因为涉及到一个元素同时拥有多个类名,还需要一个在原有类名基础上增加类名的addClassName方法。
  2. initPage, 初始化页面时,字母盘中随机生成16个字母,需要随机生成数字并输出对应字母的方法randomizeTiles; 并为每个字母(链接)和submit框绑定点击事件处理函数;
  3. randomizeTiles, 关于随机字母,书中的客户给出了26个字母在100个字母中出现的频率表,可以按照这个表初始化一个数组,然后用0~99之间的随机数(Math.floor(Math.random()*100))作为索引值从中取出对应字母;
  4. addLetter, 点击字母链接处理函数:首先找出所点击图片对应的字母;把该字母输入到输入框中,并禁用已点击过的图片链接;
  5. submitWord, 点击提交框处理函数:确认输入框不为空,将其内容发送请求到服务器确认是否合法,并设置回调函数updateScore处理服务器返回的响应;
  6. updateScore, 处理接收到的响应:确认是否合法,计算分数,把合法结果附加到单词收集框并清空输入框,最后刷新字母盘;

3) 代码要点

  1. addClassName通用函数

最初是只接收元素和类名两个参数,直接在原来的基础上添加,但后来发现这样会无休止地叠加下去,所以有必要把上次添加的相同功能的类名删除掉再重新添加。每次刷新字母盘,上次刷新添加的字母类名,以及点击之后添加的disabled类都可以删掉。所以每次添加类名前只需要保留前两个类名就可以了。我给addClassName增加了第三个可选参数number, 即添加前保留原类名的数量。

function addClassName(element,name,number) {
  var oldclass;
  if (typeof element.className !== "string") {
    oldclass = "";
  } else if (number !== undefined) { //未指定number;
    var classArr = element.className.split(/\s+/);
    classArr.length = number;
    oldclass = classArr.join(" ");
  } else {
    oldclass = element.className;
  }
  element.className = oldclass+" "+name;
}
  1. initPage初始化页面

    addLoadEvent(initPage); //页面加载完成后执行
    function initPage() {
      var letterbox = document.getElementById("letterbox");
      var letterlinks = letterbox.getElementsByTagName("a");
      var i, len = letterlinks.length;
      for (i=0; i < len; i++) {
    var letterlink = letterlinks[i];
    randomizeTiles(letterlink);
    letterlink.onclick = addLetter;
      }
      var submitbtn = document.getElementById("submit");
      submitbtn.onclick = submitWord;
    }

    在这里因为生成随机字母和绑定点击事件都需要遍历所有<a>元素,所以我让randomizeTiles接收元素作为参数而把遍历放在这个函数中,一次完成两件工作,然后再为提交按钮绑定事件。但是后来发现,当点击提交按钮之后,字母表盘需要刷新(并解除禁用重新绑定)而提交按钮不需要再绑定,所以需要把字母盘和提交按钮的点击事件分开绑定,好单独调用刷新字母盘的方法。最后我把刷新字母盘的工作都交给了randomizeTiles.

更改后

addLoadEvent(initPage);
function initPage() {
  randomizeTiles();
  var submitbtn = document.getElementById("submit");
  submitbtn.onclick = submitWord;
}
  1. randomizeTiles生成随机字母盘

    function randomizeTiles() {
      var frequencyTable= new Array("a", "a", "a", "a", "a", "a", "a", "a", "b", "c", "c", "c", "d", "d", "d",
      "e", "e", "e", "e", "e", "e", "e", "e", "e", "e", "e", "e", "f", "f", "g",
      "g", "h", "h", "h", "h", "h", "h", "i", "i", "i", "i", "i", "i", "i", "j",
      "k", "l", "l", "l", "l", "m", "m", "n", "n", "n", "n", "n", "n", "o", "o",
      "o", "o", "o", "o", "o", "o", "p", "p", "q", "q", "q", "q", "q", "q", "r",
      "r", "r", "r", "r", "r", "s", "s", "s", "s", "s", "s", "s", "s", "t", "t",
      "t", "u", "u", "v", "v", "w", "x", "y", "y", "z");
      var letterbox = document.getElementById("letterbox"),
      letterlinks = letterbox.getElementsByTagName("a"),
      i,
      len = letterlinks.length;
      for (i=0; i < len; i++) {
    var letterlink = letterlinks[i];
    var x = Math.floor(Math.random()*100);
    var classLetter = "l"+frequencyTable[x];
    addClassName(letterlink, classLetter, 2);
    letterlink.onclick = addLetter;
      }
    }

    其中数组frequencyTable是按照每个字母在100个字母中出现的频数(“客户”已提供)直接以字面量的形式创建。这样对运行来说可能是最高效的。反而最开始我思考要用什么方法通过循环复制生成数组更像是舍近求远的做法。如果输入费劲可以借助excel快速复制再整体复制文本过来,也会很方便。

  2. addLetter 点击字母的事件处理

    function addLetter() {
      var currentWord = document.getElementById("currentWord"),
      p;
      if (currentWord.childNodes.length === 0) {
    p = document.createElement("p");
    currentWord.appendChild(p);
      } else {
    p = currentWord.firstChild;
      }
      var letter = this.className.slice(-1),
      letterText = document.createTextNode(letter);
      p.appendChild(letterText);
      //禁用该图片
      this.onclick = null;
      addClassName(this, "disabled"); //用于改变图片样式
    }

    和书中不同的是,我觉得只在第一次输入字母的时候创建<p>子元素节点就可以了,后面都可以只在<p>中增加文本节点。 相对来说代码更简单一点。

  3. submitWord 点击提交
    function submitWord() {
      var currentWord = document.getElementById("currentWord");
      //如果为空则不提交
      if (currentWord.innerHTML == "" || currentWord.firstChild.innerHTML == "") {
    return false;
      } else {
    wordRequest = createRequest();
    if (wordRequest === null) {
      alert("Unable to create a request, sorry.")
      return false;
    } else {
      var url = "lookup-word.php?word="+currentWord.firstChild.innerHTML;
      wordRequest.onreadystatechange = updateScore;
      wordRequest.open("GET",url,true);
      wordRequest.send();
    }
      }
    }
  4. updateScore 处理响应
    function updateScore() {
      if (wordRequest.readyState == 4) {
    if (wordRequest.status == 0) {
      var currentWord = document.getElementById("currentWord"),
          resText = wordRequest.responseText;
      if (resText === "-1") {
        currentWord.innerHTML = "";
        alert("It\‘s not a proper word, please try again");
        randomizeTiles();
      } else if (!isNaN(resText)) {
        var wordList = document.getElementById("wordList"),
            p = currentWord.firstChild.cloneNode(true);
        wordList.appendChild(p);
        currentWord.firstChild.innerHTML = "";
        var score = document.getElementById("score"),
            oldscore = score.firstChild.nodeValue.slice(7);
            newscore = +oldscore + (+resText),
            scoreText = document.createTextNode("Score: "+newscore);
        score.replaceChild(scoreText, score.firstChild)
        randomizeTiles();
      } else {
        alert("Sorry, there is an error in the response.");
      }
    }
      }
    }

    这里先判断结果是否是-1(单词不合法),然后再判断是否是一个数值字符串(正常情况下只有这两种情况);isNaN()这个方法是会尝试对参数进行数值转换,如果失败则会返回true,表明参数不是可转化为数值的类型。另外这里我原先用innerHTML改变元素内容,后也采用操作节点的方式进行了修改。

总结

  1. 用到的DOM属性和方法:
  • childNodes
  • appendChild()
  • replaceChild()
  • firstChild()
  • getElementById()
  • getElementsByTagName()
  • createElement()
  • createTextNode()
  1. 数组和字符串的方法:
  • string.slice(index), 字符串从index位置向后截取子字符串, 如果有第二个参数则是到哪个位置停止截取;

相似的还有substring(), substr()(第二个参数指定返回字符串个数),它们接收负值参数的表现不同,slice()可接受两个负值参数;

  • string.split(), 把字符串基于指定的分隔符分割成若干子字符串返回它们组成的数组, 并可以指定返回数组的长度(第二个参数);这个在addClassName中用于把原来的className分成子类名并利用指定array.length保留一定数量的类名;

这样我可以把

    var classArr = element.className.split(/\s+/);
    classArr.length = number;

改成

    var classArr = element.className.split(/\s+/, number);

顺便复习下长得比较像的splice()方法,这是数组方法,接收的参数为(要删除的第一项位置, 要删除的项数,从该位置要插入的项……);借此可以完成对数组任意项的删除、替换以及插入任意项;返回它删除的数组(如无则空数组);值得注意的是,它操作的是原始数组。

这样上面的代码还可以改成一步到位的:

var classArr = element.className.split(/\s+/); classArr.splice(number, 2, name); //可能还有"disabled"类名所以删除2项;

不过这里因为涉及到没有指定number的情况,所以没有改用这种方式。

  1. 最后,是编程的过程中,尽量减少重复的DOM操作,一次查询或遍历最好能把一串不冲突的动作都完成。涉及到累加( +=, push等相关) 要记得看是替换还是不停累加下去;


DOM练习小记--简单的拼单词游戏

原文地址:https://www.cnblogs.com/muTing/p/9095256.html

时间: 2024-09-29 16:46:41

DOM练习小记--简单的拼单词游戏的相关文章

【代码分享】简单html5足球射门游戏分享

之前空余时间想玩玩html5, 于是使用2.2.2的cocos2d-html5 制作了个简单的足球射门游戏 ,美术是自己在纸上画完用手机拍下再ps扣的图,哈哈,赞一下自己的创意. 在我的主页可以玩这个游戏: http://www.jd85.net/ballfoot/ 很简单的几个类,就不在这里讲解了.附件里有完整项目源码和cocostudio项目可在本人发布在cocoachina论坛里的帖子内下载: http://www.cocoachina.com/bbs/read.php?tid=21394

DOM性能小记

在使用DOM操作时,同样的效果用不同的方式来实现,性能方面也会有很大的差异.尤其在移动式设备上,资源本来就很有限,一旦DOM写不好的话操作就会非常卡顿.这个周末,就写个DOM性能小记吧.错漏之处,望多指教. 1.浅说reflow 首先讲讲最近我才了解到的一个比较深入且模糊的东西——reflow. 从字面上理解的话,reflow有回流.重排的意思.它是指在DOM内容更新或增删时发生的一个响应过程,可以理解为页面内容改变了,然后发生重新排版这样的一个行为.我们可以将执行DOM操作细分为以下三个过程:

Python实现简单的猜数字游戏

Python实现简单的猜数字游戏,具体如下: 随机生成一个1-10之间的数字,让用户来猜,当猜错时,会提示猜的数字是大还是小了,直到用户猜对为止. import random secret = random.randint(1,10) #print(secret) print('------猜数字游戏!-----') guess = 0 while guess != secret: temp = input('猜数字游戏开始,请输入数字:') guess = int(temp) if guess

转 小辉_Ray DOM性能小记

在使用DOM操作时,同样的效果用不同的方式来实现,性能方面也会有很大的差异.尤其在移动式设备上,资源本来就很有限,一旦DOM写不好的话操作就会非常卡顿.这个周末,就写个DOM性能小记吧.错漏之处,望多指教. 1.浅说reflow 首先讲讲最近我才了解到的一个比较深入且模糊的东西——reflow. 从字面上理解的话,reflow有回流.重排的意思.它是指在DOM内容更新或增删时发生的一个响应过程,可以理解为页面内容改变了,然后发生重新排版这样的一个行为.我们可以将执行DOM操作细分为以下三个过程:

简单的拼成一个内有datagrid的combobox

<Border Height="22" BorderBrush="DarkGray" BorderThickness="1"> <StackPanel Orientation="Horizontal"> <TextBlock x:Name="TesTextBox" Height="20" Width="154"/> <Butt

c语言:简单飞机射击小游戏

c语言:简单飞机射击小游戏 使用c语言编写一个打飞机小游戏,使用键盘按键来进行游戏,操作方法是"a""d""w"或者"←""↑""→"来控制攻击.左.右,击中敌机可获得积分,被敌机撞中死亡一次,每次游戏有3次机会,机会用光则游戏结束,之后可选择是否重新开始游戏. 改进:增加了颜色函数,使得游戏看起来更加的舒适:增加了终止函数,使游戏在死亡三次后自动结束游戏,并且可以选择是否重新开始游戏:

(NO.00003)iOS游戏简单的机器人投射游戏成形记(一)

这是一个简单的机器人投射游戏,主要来熟悉物理引擎的一些东西.你可以把它认为是机器人投篮;尽管投出的是抛物线,但不是篮球而是子弹,速度也较快. 游戏玩法是玩家选择机器人,移动机器人手臂瞄准篮框,然后发射子弹.如果子弹掉入篮框则得分.子弹耗完或时间耗完则游戏结束. 游戏中主要玩法的变化如下: 1.可以通过修改过关条件,比如限制子弹数量,限制剩余时间,要求必须达到一定分数等增加或降低难度. 2.可以通过Level场景的布局以及篮框的移动路线来增加难度,别忘了场景中机器人的位置也是可以变化的,而且谁说场

简单的三子棋游戏

简单的三子棋游戏,我能力有限把电脑玩家的走子写的比较简单. #include<stdio.h> void qipan() { printf("_ _|_ _|_ _\n"); printf("_ _|_ _|_ _\n"); printf("_ _|_ _|_ _\n"); } void player_and_pcdo() { int i = 0, j = 0; static char a[3][3] = { ' ', ' ', '

一个简单的抽奖转盘游戏

在一个项目中要做一个游戏,在这个过程中做了一个简单的9宫格抽奖游戏.大体思路是,点击开始按钮,游戏开始.由一个逐步递增参数 drawStep 来控制格子的背景颜色的改变,游戏停止的位置参数 stopPosition 由随机函数生成,以此来控制格子停止的位置.游戏转动一圈是8个格子,5圈就是40.easeTime参数模拟格子转动的缓步启动和缓步停止. <!doctype html> <html> <head> <meta charset="gbk"