JS井字游戏中令人讨厌的错误

时间:2018-09-06 04:27:10

标签: javascript html css

const winCombos = [
     [0, 1, 2],
     [3, 4, 5],
     [6, 7, 8],
     [0, 3, 6],
     [1, 4, 7],
     [2, 5, 8],
     [0, 4, 8],
     [6, 4, 2]
];

let playerTurn = 'X';

for(let i = 0; i < 9; i++) {
     const cell = document.getElementById('c' + i);
     cell.addEventListener('click', startGame);
     function startGame() {
          cell.innerHTML = playerTurn;
          if(checkForWin()) {
               alert('game over');
          }
     }
}

function checkForWin() {
     for(let i = 0; i < winCombos.length; i++) {
          const c0 = document.getElementById('c' + winCombos[i][0]);
          const c1 = document.getElementById('c' + winCombos[i][1]);
          const c2 = document.getElementById('c' + winCombos[i][2]);
          if(c0.innerHTML===playerTurn&&
             c1.innerHTML===playerTurn&&
             c2.innerHTML===playerTurn) {
               return true;
          } else {
               return false;
          }
     }
}
td {
	border:  2px solid #333;
	height:  100px;
	width:  100px;
	text-align:  center;
	vertical-align:  middle;
	font-family:  "Comic Sans MS", cursive, sans-serif;
	font-size:  70px;
	cursor: pointer;
}

table {
	border-collapse: collapse;
	position: absolute;
	left: 50%;
	margin-left: -155px;
	top: 220px;
}

table tr:first-child td {
	border-top: 0;
}

table tr:last-child td {
	border-bottom: 0;
}

table tr td:first-child {
	border-left: 0;
}

table tr td:last-child {
	border-right: 0;
}

.endgame {
  display: none;
  width: 200px;
  top: 120px;
  background-color: red;
  position: absolute;
  left: 50%;
  margin-left: -100px;
  padding-top: 50px;
  padding-bottom: 50px;
  text-align: center;
  border-radius: 5px;
  color: white;
  font-size: 2em;
}
<!DOCTYPE html>
<html>
<head>
	<title>tic tac toe</title>
	<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
	<table>
		<tr>
			<td class="cell" id="c0"></td>
			<td class="cell" id="c1"></td>
			<td class="cell" id="c2"></td>
		</tr>
		<tr>
			<td class="cell" id="c3"></td>
			<td class="cell" id="c4"></td>
			<td class="cell" id="c5"></td>
		</tr>
		<tr>
			<td class="cell" id="c6"></td>
			<td class="cell" id="c7"></td>
			<td class="cell" id="c8"></td>
		</tr>
		<div class="endgame">
			<div class="text">Cant see this text</div>
		</div>
	</table>

	<script type="text/javascript" src="script2.js"></script>
</body>
</html>

嘿,我正在尝试制作一个简单的JS tic tac toe游戏。现在,我要做的只是第3行'X'向上,一旦放置第3个X以构成获胜组合,我就希望收到警报消息。但是,当您单击以放置第三个“ X”以赢得游戏时,警报消息立即显示,然后显示了第三个“ X”?为什么会这样,谢谢您的帮助。

2 个答案:

答案 0 :(得分:1)

这里要了解的重要事项是javascript在单线程模型中运行。现在,让我们看一下您的代码:

cell.addEventListener('click', startGame);
function startGame() {
     cell.innerHTML = playerTurn;
     if(checkForWin()) {
          alert('game over');
     }
}

在click事件处理程序中,您需要执行以下操作:

  1. 更新单元格的内容
  2. 检查获胜的举动
  3. 如果最后一招是获胜举动,则显示警报

从CPU的角度来看,所有这些“动作”都是顺序执行的,因此您希望单元的内容首先被呈现,对吧?但是,事实并非如此。当您设置单元格的innerHTML时,DOM将变脏,并且更改将在下一个渲染线程循环中呈现,这将在您的JavaScript线程变为空闲状态时发生。但是,您仍然有待执行的操作2和3,因此将首先执行它们,然后在javascript线程变为空闲时,您的渲染线程将占据主导地位并更新屏幕。

我希望您现在对javascript的工作方式有一个清晰的认识。

现在,要解决您的问题,您可以在显示警报之前设置次超时,以便在显示警报之前更新页面。

答案 1 :(得分:0)

点击发生后,requestAnimationFrame侦听器正在同步运行 ,并且alert 阻止了浏览器 -它阻止了进一步的Javascript和从重新渲染开始直到警报被消除为止。该功能运行时,浏览器还没有时间重新绘制。

您可以使用x来确保 window.requestAnimationFrame(() => { setTimeout(() => { alert('game over'); }); }); 仅在之后显示,浏览器完成了重新渲染以显示新的const winCombos = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [6, 4, 2] ]; let playerTurn = 'X'; for (let i = 0; i < 9; i++) { const cell = document.getElementById('c' + i); cell.addEventListener('click', startGame); function startGame() { cell.innerHTML = playerTurn; if (checkForWin()) { window.requestAnimationFrame(() => { setTimeout(() => { alert('game over'); }); }); } } } function checkForWin() { for (let i = 0; i < winCombos.length; i++) { const c0 = document.getElementById('c' + winCombos[i][0]); const c1 = document.getElementById('c' + winCombos[i][1]); const c2 = document.getElementById('c' + winCombos[i][2]); if (c0.innerHTML === playerTurn && c1.innerHTML === playerTurn && c2.innerHTML === playerTurn) { return true; } else { return false; } } }

td {
  border: 2px solid #333;
  height: 100px;
  width: 100px;
  text-align: center;
  vertical-align: middle;
  font-family: "Comic Sans MS", cursive, sans-serif;
  font-size: 70px;
  cursor: pointer;
}

table {
  border-collapse: collapse;
  position: absolute;
  left: 50%;
  margin-left: -155px;
  top: 220px;
}

table tr:first-child td {
  border-top: 0;
}

table tr:last-child td {
  border-bottom: 0;
}

table tr td:first-child {
  border-left: 0;
}

table tr td:last-child {
  border-right: 0;
}

.endgame {
  display: none;
  width: 200px;
  top: 120px;
  background-color: red;
  position: absolute;
  left: 50%;
  margin-left: -100px;
  padding-top: 50px;
  padding-bottom: 50px;
  text-align: center;
  border-radius: 5px;
  color: white;
  font-size: 2em;
}

<!DOCTYPE html>
<html>

<head>
  <title>tic tac toe</title>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
  <table>
    <tr>
      <td class="cell" id="c0"></td>
      <td class="cell" id="c1"></td>
      <td class="cell" id="c2"></td>
    </tr>
    <tr>
      <td class="cell" id="c3"></td>
      <td class="cell" id="c4"></td>
      <td class="cell" id="c5"></td>
    </tr>
    <tr>
      <td class="cell" id="c6"></td>
      <td class="cell" id="c7"></td>
      <td class="cell" id="c8"></td>
    </tr>
    <div class="endgame">
      <div class="text">Cant see this text</div>
    </div>
  </table>

  <script type="text/javascript" src="script2.js"></script>
</body>

</html>
alert
for

或者,甚至更好的是,使用适当的模态。 checkForWin和相关的阻止功能非常不友好。

还请注意,将函数声明悬挂在其包含函数(或顶层)的顶部(或顶层)–不应位于const winCombos = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [6, 4, 2] ]; function startGame() { cell.innerHTML = playerTurn; if (checkForWin()) { document.querySelector('.endgame').style.display = 'block'; } } let playerTurn = 'X'; for (let i = 0; i < 9; i++) { const cell = document.getElementById('c' + i); cell.addEventListener('click', startGame); } const checkForWin = () => winCombos.some((combo) => ( combo.map(i => document.getElementById('c' + i)) .every(cell => cell.textContent === playerTurn) ))块内。

如果您改为使用数组方法,还可以将td { border: 2px solid #333; height: 100px; width: 100px; text-align: center; vertical-align: middle; font-family: "Comic Sans MS", cursive, sans-serif; font-size: 70px; cursor: pointer; } table { border-collapse: collapse; position: absolute; left: 50%; margin-left: -155px; top: 220px; } table tr:first-child td { border-top: 0; } table tr:last-child td { border-bottom: 0; } table tr td:first-child { border-left: 0; } table tr td:last-child { border-right: 0; } .endgame { display: none; width: 200px; top: 120px; background-color: red; position: absolute; left: 50%; margin-left: -100px; padding-top: 50px; padding-bottom: 50px; text-align: center; border-radius: 5px; color: white; font-size: 2em; }函数减少到三行:

<table>
  <tr>
    <td class="cell" id="c0"></td>
    <td class="cell" id="c1"></td>
    <td class="cell" id="c2"></td>
  </tr>
  <tr>
    <td class="cell" id="c3"></td>
    <td class="cell" id="c4"></td>
    <td class="cell" id="c5"></td>
  </tr>
  <tr>
    <td class="cell" id="c6"></td>
    <td class="cell" id="c7"></td>
    <td class="cell" id="c8"></td>
  </tr>
  <div class="endgame">
    <div class="text">Game is over</div>
  </div>
</table>
for
void DisplayDirectoryContents()
{
    // Get files and folders from directory
    list<File> files = this->getFiles();

    // Iterate over each item in the list.
    // Use a reference to avoid copying of the items.
    for ( File& file : files )
    {
        cout << file.getFileName();
    }
}