当前功能代码完成之前被调用的功能

时间:2018-09-19 01:41:15

标签: javascript

使用JS Fiddle实现了“ 5个猎人,3只兔子”的问题,在这里进行了描述:https://twitter.com/Mathgarden/status/1039247616616194048

我的代码在这里:https://jsfiddle.net/iPrash/o037fpam/

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <title>Rabbit Hunters</title>
    <link rel="stylesheet" href="htmltable.css">

    <script language="JavaScript">
      var size = 5;
      var hunters = new Array();
      var arena = new Array(side, side);

      function hunter(row, col) {
        this.row = row;
        this.col = col;
      }

      function hunter(row, col) {
        this.row = row;
        this.col = col;
      }

      function resetArena() {
        hunters = [];
        redrawArena();
      }

      function generate_table() {
        // get the reference for the body
        var body = document.getElementsByTagName("body")[0];

        // creates a <table> element and a <tbody> element
        var tbl = document.createElement("table");
        tbl.setAttribute("class", "huntertable");
        var tblBody = document.createElement("tbody");

        // creating all cells
        for (var i = 0; i < size; i++) {
          // creates a table row
          var row = document.createElement("tr");

          for (var j = 0; j < size; j++) {
            // Create a <td> element and a text node, make the text
            // node the contents of the <td>, and put the <td> at
            // the end of the table row
            var cell = document.createElement("td");
            cell.addEventListener("click", cellClicked);
            cell.bgColor = "green";
            cell.innerHTML = "O";
            row.appendChild(cell);
          }

          // add the row to the end of the table body
          tblBody.appendChild(row);
        }

        // put the <tbody> in the <table>
        tbl.appendChild(tblBody);
        // appends <table> into <body>
        body.appendChild(tbl);
        // sets the border attribute of tbl to 2;
        tbl.setAttribute("border", "5");
      }

      function cellClicked() {

        var cellRow = this.parentNode.rowIndex;
        var cellCol = this.cellIndex;
        var cHunter = new hunter(cellRow, cellCol);

        if (exists(cHunter)) {
          // remove the hunter 
          remove(cHunter);

        } else {

          if (hunters.length == 5) {
            alert("A maximum of 5 hunters are allowed!");
            return;
          }

          hunters.push(cHunter);
          redrawArena();
        }
      }

      function exists(hunter) {
        for (var i = 0; i < hunters.length; i++) {
          if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col))
            return true;
        }
        return false;

      }

      function remove(hunter) {
        for (var i = 0; i < hunters.length; i++) {
          if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col)) {
            hunters.splice(i, 1);
            break;
          }
        }
        redrawArena();
      }

      function redrawArena() {

        var arenaTable = document.getElementsByTagName("tbl")[0];
        var arenaTBody = document.getElementsByTagName("tbody")[0];

        // reset arena
        for (var rowi = 0; rowi < size; rowi++) {
          for (var coli = 0; coli < size; coli++) {
            rRow = arenaTBody.getElementsByTagName("tr")[rowi];
            rCell = rRow.getElementsByTagName("td")[coli];
            rCell.innerHTML = "O";
            rCell.bgColor = "green";
          }
        }

        for (var hunterIndex = 0; hunterIndex < hunters.length; hunterIndex++) {
          // for each hunter mark the attacked territory:
          hunterRow = hunters[hunterIndex].row;
          hunterCol = hunters[hunterIndex].col;

          huntRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
          huntCell = huntRow.getElementsByTagName("td")[hunterCol];
          huntCell.innerHTML = "H";
          huntCell.bgColor = "red";

          // horizontal and vertical
          for (var i = 0; i < size; i++) {
            hRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
            hCell = hRow.getElementsByTagName("td")[i];
            hCell.bgColor = "red";

            vRow = arenaTBody.getElementsByTagName("tr")[i];
            vCell = vRow.getElementsByTagName("td")[hunterCol];
            vCell.bgColor = "red";
          }

          // diagonals
          for (var i = 1; i < size; i++) {

            if (((hunterRow + i) < size) && ((hunterCol + i) < size)) {
              dRow1 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
              dCell1 = dRow1.getElementsByTagName("td")[hunterCol + i];
              dCell1.bgColor = "red";
            }

            if (((hunterRow - i) >= 0) && ((hunterCol - i) >= 0)) {
              dRow2 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
              dCell2 = dRow2.getElementsByTagName("td")[hunterCol - i];
              dCell2.bgColor = "red";
            }

            if (((hunterRow + i) < size) && ((hunterCol - i) >= 0)) {
              dRow3 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
              dCell3 = dRow3.getElementsByTagName("td")[hunterCol - i];
              dCell3.bgColor = "red";
            }

            if (((hunterRow - i) >= 0) && ((hunterCol + i) < size)) {
              dRow4 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
              dCell4 = dRow4.getElementsByTagName("td")[hunterCol + i];
              dCell4.bgColor = "red";
            }
          }
        }
        alert("Checking for win ...");
        checkWin();
      }

      function checkWin() {
        // check arena for 5 hunters and 3 rabbits...
        if (hunters.length < 5)
            return;

                var arenaTable = document.getElementsByTagName("tbl")[0];
                var arenaTBody = document.getElementsByTagName("tbody")[0];

        var rabbits = 0;
        for (var rowi = 0; rowi < size; rowi++) {
          for (var coli = 0; coli < size; coli++) {
            rRow = arenaTBody.getElementsByTagName("tr")[rowi];
            rCell = rRow.getElementsByTagName("td")[coli];
            if (rCell.bgColor == "green") {
                rabbits++;            
            }

          }
        }
        if (rabbits == 3)
            alert("Congrats! You did it!")
      }

    </script>
  </head>

  <body onload="generate_table()">
    <h1>Rabbit Hunters</h1>
    <p>
      <ol>
        <li>The grid below represents a forest filled with rabbits (green).</li>
        <li>Hunters can attack horizontally and diagonally in all directions (like a chess queen).</li>
        <li>Once placed, hunters will kill all rabbits in their lines of sight (try clicking!).</li>
        <li>To remove hunters just click on them again.</li>
        <li>The Reset button clears the whole forest.</li>
      </ol>
      <strong>Can you place 5 hunters on the grid below so that they spare 3 rabbits (three green squares should remain)?</strong>
    </p>
    <p>
      <input type="button" value="Reset" onclick="resetArena()" />
    </p>
  </body>

</html>

=====================

我的问题是:为什么在绘制最后一个猎人之前(或竞技场被完全重画之前)显示“正在检查获胜...”警报。我添加了此调试警报,因为即使我将checkWin()函数称为之后,重绘循环也已完全完成,它似乎想首先开始执行checkWin()。因此,只有在警报发出后,最后一次单击的猎人方格才变成“ H”,而我希望它在检查获胜之前就变成了。

谢谢!

1 个答案:

答案 0 :(得分:1)

关于问题

在功能checkWin中的任何其他代码之前,并未真正调用您的功能redrawArena。所描述的问题是由于代码的使用引起的,该代码阻塞了浏览器的主UI线程,该线程用于更新渲染的文档并执行其JS代码(工人除外)。因此,如果您动态更改文档中某些元素的属性(如CSS样式),浏览器可能不会立即重新绘制(或重排)文档,通常会在调用堆栈中已经存在的所有JS函数弹出后(即返回)值)。让我们将函数代码的某些部分称为“阻止代码”,如果它可以防止该函数长时间返回其值(足够长的时间以使网站对用户无响应的话)。阻塞代码的最常见来源是同步XMLHttpRequest,本地JS对话框,长时间运行的循环(例如,一些繁重的计算)等。

这是一个简单的示例,展示了更长的运行循环(第一次更改颜色后)将如何阻止文档的重新绘制,因此您将永远不会看到“忙碌状态”(红色):

#status {
  display: inline-block;
  width: 10px;
  height: 10px;
  background-color: gray;
}
<!DOCTYPE html>
<html>
  <head>
    <script>
      function start(){
        setStatus('red');
        compute();
      }
      
      function compute(){
        var a = [];
        for (var i = 0; i < 10; i+=0.000001){
          a.push(Math.sin(i) + Math.cos(i));
        }
        setStatus('green');
      }
      
      function setStatus(color){
        document.getElementById('status').style.backgroundColor = color;
      }
    </script>
  </head>
  <body>
    <div>Status: <div id="status"></div></div>
    <button onclick="start()">Compute</button>
    <button onclick="setStatus(null)">Reset</button>
  </body>
</html>

警报模式

很显然,代码中的这种线程阻塞行为只能由alert()调用引起。但是,这里出现一个问题-您使用哪个浏览器测试代码?根据{{​​3}}:

  
      
  1. 向用户显示消息。
  2.   
  3. (可选)在等待用户确认消息时暂停。
  4.   

然后是alert spec

  
      
  1. 如有必要,更新任何文档或浏览上下文的呈现或用户界面以反映当前状态。
  2.   
  3. 等待直到达到条件目标。当用户代理具有暂停的任务时,相应的事件循环不得运行其他任务,并且当前正在运行的任务中的任何脚本都必须阻止。用户代理在暂停时应保持对用户输入的响应,尽管容量会降低,因为事件循环不会做任何事情。
  4.   

警报不应阻止呈现(UI可能会在暂停期间刷新),并且从理论上讲,它在等待用户时不需要阻止任何后续的JS代码(因为规范说暂停是可选的)。

但是,浏览器并不总是遵循规范(在暂停的情况下,允许并鼓励进行实验),因此我决定在当前计算机(Win7)上可用的几种浏览器上测试您的代码:

|-------------|---------------------|-----------------------|
|             |                     |                       |
|   browser   |  blocks UI refresh  |  executes subsequent  |
|             |     during alert    |   code during alert   |
|             |                     |                       |
|-------------|---------------------|-----------------------|
| Chrome 69.0 |         yes         |          no*          |
|-------------|---------------------|-----------------------|
| Opera 55.0  |         yes         |          no           |
|-------------|---------------------|-----------------------|
| FF Dev 63.0 |         no          |          no           |
|-------------|---------------------|-----------------------|
| FF 62.0     |         no          |          no           |
|-------------|---------------------|-----------------------|
| FF 60.2     |         no          |          no           |
|-------------|---------------------|-----------------------|
| FF 52.9     |         no          |          no           |
|-------------|---------------------|-----------------------|
| IE 11       |         no          |          no           |
|-------------|---------------------|-----------------------|

从上表中可以看到,我仅在Chrome和Opera(相同的渲染引擎,当前最新的稳定版本)中遇到了描述的问题。在警报期间,没有经过测试的浏览器将不会运行任何后续的JS代码,但是,如果在显示第一个警报(*之前触发了这些事件,则Chrome似乎会将输入事件的回调推入调用堆栈。例如。如果您一次又一次地单击表格单元格,则函数cellClicked将被调用多次(因此,如果您确认第一个警报,Chrome将更新UI状态并显示另一个警报)。表中的任何其他浏览器似乎都没有这种行为。

可能的解决方案

要避免潜在的不必要的未决UI更新阻塞,最直接的解决方案是在执行阻塞代码之前,让浏览器有时间执行更新。这可以通过将警报和后续代码移入异步回调(例如,通过使用setTimeout函数。在以下代码段中,我将函数checkWin()中的警报“ Checking for win ...”和redrawArena的调用移到了其中添加的setTimeout函数的回调中。这将允许浏览器在显示任何警报之前刷新您的表,从而绕过受影响的浏览器中有害的UI阻止效果。您还可以使用setTimeout的 delay 参数来查找最小值,该最小值将允许在所有目标浏览器中触发UI刷新。

.huntertable tr {
  cursor: pointer;
}

.huntertable td {
  font-size: 40px;
  text-align: center;
}
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <title>Rabbit Hunters</title>

    <script>
      var size = 5;
      var hunters = new Array();

      function hunter(row, col) {
        this.row = row;
        this.col = col;
      }

      function resetArena() {
        hunters = [];
        redrawArena();
      }

      function generate_table() {
        // get the reference for the body
        var body = document.getElementsByTagName("body")[0];

        // creates a <table> element and a <tbody> element
        var tbl = document.createElement("table");
        tbl.setAttribute("class", "huntertable");
        var tblBody = document.createElement("tbody");

        // creating all cells
        for (var i = 0; i < size; i++) {
          // creates a table row
          var row = document.createElement("tr");

          for (var j = 0; j < size; j++) {
            // Create a <td> element and a text node, make the text
            // node the contents of the <td>, and put the <td> at
            // the end of the table row
            var cell = document.createElement("td");
            cell.addEventListener("click", cellClicked);
            cell.bgColor = "green";
            cell.innerHTML = "O";
            row.appendChild(cell);
          }

          // add the row to the end of the table body
          tblBody.appendChild(row);
        }

        // put the <tbody> in the <table>
        tbl.appendChild(tblBody);
        // appends <table> into <body>
        body.appendChild(tbl);
        // sets the border attribute of tbl to 2;
        tbl.setAttribute("border", "5");
      }

      function cellClicked() {

        var cellRow = this.parentNode.rowIndex;
        var cellCol = this.cellIndex;
        var cHunter = new hunter(cellRow, cellCol);

        if (exists(cHunter)) {
          // remove the hunter 
          remove(cHunter);

        } else {

          if (hunters.length == 5) {
            alert("A maximum of 5 hunters are allowed!");
            return;
          }

          hunters.push(cHunter);
          redrawArena();
        }
      }

      function exists(hunter) {
        for (var i = 0; i < hunters.length; i++) {
          if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col))
            return true;
        }
        return false;

      }

      function remove(hunter) {
        for (var i = 0; i < hunters.length; i++) {
          if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col)) {
            hunters.splice(i, 1);
            break;
          }
        }
        redrawArena();
      }

      function redrawArena() {

        var arenaTable = document.getElementsByTagName("tbl")[0];
        var arenaTBody = document.getElementsByTagName("tbody")[0];

        // reset arena
        for (var rowi = 0; rowi < size; rowi++) {
          for (var coli = 0; coli < size; coli++) {
            rRow = arenaTBody.getElementsByTagName("tr")[rowi];
            rCell = rRow.getElementsByTagName("td")[coli];
            rCell.innerHTML = "O";
            rCell.bgColor = "green";
          }
        }

        for (var hunterIndex = 0; hunterIndex < hunters.length; hunterIndex++) {
          // for each hunter mark the attacked territory:
          hunterRow = hunters[hunterIndex].row;
          hunterCol = hunters[hunterIndex].col;

          huntRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
          huntCell = huntRow.getElementsByTagName("td")[hunterCol];
          huntCell.innerHTML = "H";
          huntCell.bgColor = "red";

          // horizontal and vertical
          for (var i = 0; i < size; i++) {
            hRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
            hCell = hRow.getElementsByTagName("td")[i];
            hCell.bgColor = "red";

            vRow = arenaTBody.getElementsByTagName("tr")[i];
            vCell = vRow.getElementsByTagName("td")[hunterCol];
            vCell.bgColor = "red";
          }

          // diagonals
          for (var i = 1; i < size; i++) {

            if (((hunterRow + i) < size) && ((hunterCol + i) < size)) {
              dRow1 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
              dCell1 = dRow1.getElementsByTagName("td")[hunterCol + i];
              dCell1.bgColor = "red";
            }

            if (((hunterRow - i) >= 0) && ((hunterCol - i) >= 0)) {
              dRow2 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
              dCell2 = dRow2.getElementsByTagName("td")[hunterCol - i];
              dCell2.bgColor = "red";
            }

            if (((hunterRow + i) < size) && ((hunterCol - i) >= 0)) {
              dRow3 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
              dCell3 = dRow3.getElementsByTagName("td")[hunterCol - i];
              dCell3.bgColor = "red";
            }

            if (((hunterRow - i) >= 0) && ((hunterCol + i) < size)) {
              dRow4 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
              dCell4 = dRow4.getElementsByTagName("td")[hunterCol + i];
              dCell4.bgColor = "red";
            }
          }
        }
        
        setTimeout(function() {
          alert("Checking for win ...");
       	  checkWin();
         },20);
      }
      
      function checkWin() {
        // check arena for 5 hunters and 3 rabbits...
        if (hunters.length < 5)
        	return;
          
				var arenaTable = document.getElementsByTagName("tbl")[0];
				var arenaTBody = document.getElementsByTagName("tbody")[0];

        var rabbits = 0;
        for (var rowi = 0; rowi < size; rowi++) {
          for (var coli = 0; coli < size; coli++) {
            rRow = arenaTBody.getElementsByTagName("tr")[rowi];
            rCell = rRow.getElementsByTagName("td")[coli];
            if (rCell.bgColor == "green") {
            	rabbits++;            
            }
            	
          }
        }
        if (rabbits == 3)
        	alert("Congrats! You did it!")
      }

    </script>
  </head>

  <body onload="generate_table()">
    <h1>Rabbit Hunters</h1>
    <p>
      <ol>
        <li>The grid below represents a forest filled with rabbits (green).</li>
        <li>Hunters can attack horizontally and diagonally in all directions (like a chess queen).</li>
        <li>Once placed, hunters will kill all rabbits in their lines of sight (try clicking!).</li>
        <li>To remove hunters just click on them again.</li>
        <li>The Reset button clears the whole forest.</li>
      </ol>
      <strong>Can you place 5 hunters on the grid below so that they spare 3 rabbits (three green squares should remain)?</strong>
    </p>
    <p>
      <input type="button" value="Reset" onclick="resetArena()" />
    </p>
  </body>

</html>

但是,在大多数情况下(如您的情况),最好将这种输出直接显示在文档中。这样,您的消息将在下次UI刷新时与DOM中的其他更改一起显示。借助HTML,CSS和JS,您可以创建用于显示消息的个人方法(包括自定义模式,信息栏等)。在下面的代码段中,我创建了一个非常简单的示例,您的应用如何将消息输出给用户。基本上,我添加了用于显示消息的附加div,创建了两个用于在此div中显示/删除消息的功能(showMessage / removeMessage),并用showMessage调用替换了警报(并删除了无意义的警报“检查胜利” ...”)。

.huntertable tr {
  cursor: pointer;
}

.huntertable td {
  font-size: 40px;
  text-align: center;
}

#infobar {
  color: red;
  font-weight: bold;
  margin-bottom: 10px;
  visibility: hidden;
  min-height: 20px;
}
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <title>Rabbit Hunters</title>

    <script>
      var size = 5;
      var hunters = new Array();

      function hunter(row, col) {
        this.row = row;
        this.col = col;
      }
      
      function showMessage(msgText) {
        var infobar = document.getElementById('infobar');
        infobar.innerHTML = msgText;
        infobar.style.visibility = 'visible';
      }
      
      function removeMessage() {
        var infobar = document.getElementById('infobar');
        infobar.innerHTML = '';
        infobar.style.visibility = null;
      }
      
      function resetArena() {
        removeMessage();
        hunters = [];
        redrawArena();
      }

      function generate_table() {
        // get the reference for the body
        var body = document.getElementsByTagName("body")[0];

        // creates a <table> element and a <tbody> element
        var tbl = document.createElement("table");
        tbl.setAttribute("class", "huntertable");
        var tblBody = document.createElement("tbody");

        // creating all cells
        for (var i = 0; i < size; i++) {
          // creates a table row
          var row = document.createElement("tr");

          for (var j = 0; j < size; j++) {
            // Create a <td> element and a text node, make the text
            // node the contents of the <td>, and put the <td> at
            // the end of the table row
            var cell = document.createElement("td");
            cell.addEventListener("click", cellClicked);
            cell.bgColor = "green";
            cell.innerHTML = "O";
            row.appendChild(cell);
          }

          // add the row to the end of the table body
          tblBody.appendChild(row);
        }

        // put the <tbody> in the <table>
        tbl.appendChild(tblBody);
        // appends <table> into <body>
        body.appendChild(tbl);
        // sets the border attribute of tbl to 2;
        tbl.setAttribute("border", "5");
      }

      function cellClicked() {
        removeMessage();
        
        var cellRow = this.parentNode.rowIndex;
        var cellCol = this.cellIndex;
        var cHunter = new hunter(cellRow, cellCol);

        if (exists(cHunter)) {
          // remove the hunter 
          remove(cHunter);

        } else {

          if (hunters.length == 5) {
            showMessage("A maximum of 5 hunters are allowed!");
            return;
          }

          hunters.push(cHunter);
          redrawArena();
        }
      }

      function exists(hunter) {
        for (var i = 0; i < hunters.length; i++) {
          if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col))
            return true;
        }
        return false;

      }

      function remove(hunter) {
        for (var i = 0; i < hunters.length; i++) {
          if ((hunters[i].row == hunter.row) && (hunters[i].col == hunter.col)) {
            hunters.splice(i, 1);
            break;
          }
        }
        redrawArena();
      }

      function redrawArena() {

        var arenaTable = document.getElementsByTagName("tbl")[0];
        var arenaTBody = document.getElementsByTagName("tbody")[0];

        // reset arena
        for (var rowi = 0; rowi < size; rowi++) {
          for (var coli = 0; coli < size; coli++) {
            rRow = arenaTBody.getElementsByTagName("tr")[rowi];
            rCell = rRow.getElementsByTagName("td")[coli];
            rCell.innerHTML = "O";
            rCell.bgColor = "green";
          }
        }

        for (var hunterIndex = 0; hunterIndex < hunters.length; hunterIndex++) {
          // for each hunter mark the attacked territory:
          hunterRow = hunters[hunterIndex].row;
          hunterCol = hunters[hunterIndex].col;

          huntRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
          huntCell = huntRow.getElementsByTagName("td")[hunterCol];
          huntCell.innerHTML = "H";
          huntCell.bgColor = "red";

          // horizontal and vertical
          for (var i = 0; i < size; i++) {
            hRow = arenaTBody.getElementsByTagName("tr")[hunterRow];
            hCell = hRow.getElementsByTagName("td")[i];
            hCell.bgColor = "red";

            vRow = arenaTBody.getElementsByTagName("tr")[i];
            vCell = vRow.getElementsByTagName("td")[hunterCol];
            vCell.bgColor = "red";
          }

          // diagonals
          for (var i = 1; i < size; i++) {

            if (((hunterRow + i) < size) && ((hunterCol + i) < size)) {
              dRow1 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
              dCell1 = dRow1.getElementsByTagName("td")[hunterCol + i];
              dCell1.bgColor = "red";
            }

            if (((hunterRow - i) >= 0) && ((hunterCol - i) >= 0)) {
              dRow2 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
              dCell2 = dRow2.getElementsByTagName("td")[hunterCol - i];
              dCell2.bgColor = "red";
            }

            if (((hunterRow + i) < size) && ((hunterCol - i) >= 0)) {
              dRow3 = arenaTBody.getElementsByTagName("tr")[hunterRow + i];
              dCell3 = dRow3.getElementsByTagName("td")[hunterCol - i];
              dCell3.bgColor = "red";
            }

            if (((hunterRow - i) >= 0) && ((hunterCol + i) < size)) {
              dRow4 = arenaTBody.getElementsByTagName("tr")[hunterRow - i];
              dCell4 = dRow4.getElementsByTagName("td")[hunterCol + i];
              dCell4.bgColor = "red";
            }
          }
        }
       	checkWin();
      }
      
      function checkWin() {
        // check arena for 5 hunters and 3 rabbits...
        if (hunters.length < 5)
        	return;
          
				var arenaTable = document.getElementsByTagName("tbl")[0];
				var arenaTBody = document.getElementsByTagName("tbody")[0];

        var rabbits = 0;
        for (var rowi = 0; rowi < size; rowi++) {
          for (var coli = 0; coli < size; coli++) {
            rRow = arenaTBody.getElementsByTagName("tr")[rowi];
            rCell = rRow.getElementsByTagName("td")[coli];
            if (rCell.bgColor == "green") {
            	rabbits++;            
            }
            	
          }
        }
        if (rabbits == 3)
        	showMessage("Congrats! You did it!");
      }

    </script>
  </head>

  <body onload="generate_table()">
    <h1>Rabbit Hunters</h1>
    <p>
      <ol>
        <li>The grid below represents a forest filled with rabbits (green).</li>
        <li>Hunters can attack horizontally and diagonally in all directions (like a chess queen).</li>
        <li>Once placed, hunters will kill all rabbits in their lines of sight (try clicking!).</li>
        <li>To remove hunters just click on them again.</li>
        <li>The Reset button clears the whole forest.</li>
      </ol>
      <strong>Can you place 5 hunters on the grid below so that they spare 3 rabbits (three green squares should remain)?</strong>
    </p>
    <p>
      <input type="button" value="Reset" onclick="resetArena()" />
    </p>
    <div id="infobar"></div>
  </body>

</html>

TL; DR

通常,您应该避免使用JS警报来显示应用程序的常规文本输出或对其进行调试。警报可能会阻止某些浏览器中的UI刷新,直到用户确认它们为止,并且它们可能会被浏览器阻止,从而使它们不可靠地显示重要信息。如果需要向用户显示消息,则应创建一种或多种方法在文档中动态显示消息(实际上可以在Web上找到许多不同的解决方案)。为了进行调试,您应该使用集成在每个现代浏览器中的“开发工具”面板以及pause spec对象中的方法。如果您仍然坚持在应用程序中使用警报,并且由于警报而遇到不必要的页面UI刷新阻塞或不相关的代码,则可能需要将其放入异步回调中。