从事件函数更改全局变量
Change global variable from event function
我正在努力学习Javascript。
我在 Youtube 上看了一些 Javascript 课程,正在尝试创建我的第一个项目。
项目应该是简单的井字游戏。
功能:
第一次点击框应该填写 "X"
第二次单击另一个框应填充 "Y"
第三次单击另一个框应再次填充 "X",依此类推,直到所有框都被字符填充。
有我的代码
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="main">
<table>
<tr>
<td class="b1-1"></td>
<td class="b1-2"></td>
<td class="b1-3"></td>
</tr>
<tr>
<td class="b2-1"></td>
<td class="b2-2"></td>
<td class="b2-3"></td>
</tr>
<tr>
<td class="b3-1"></td>
<td class="b3-2"></td>
<td class="b3-3"></td>
</tr>
</table>
</div>
<script src="script.js" type="text/javascript"></script>
</body>
</html>
CSS
.main {
padding: 100px 0;
width: 360px;
margin: 0 auto;
}
table, tbody {
margin: 0;
padding: 0;
width: 360px;
height: 360px;
}
tr {
width:360px;
height: 120px;
margin: 0;
padding: 0;
}
td {
text-align: center;
width:120px;
height: 120px;
border: 1px solid #333;
margin: 0;
padding: 0;
font-size: 50px;
}
Javascript
var action = document.querySelectorAll('td');
var gameState = 0;
for (var i = 0; i <= action.length - 1; i++) {
getClassName = "." + action[i].classList.value;
if (gameState === 0) {
document.querySelector(getClassName).addEventListener("click", chasviX(i));
} else {
document.querySelector(getClassName).addEventListener("click", chasviO(i));
}
}
function chasviX(cord) {
document.querySelector("." + action[cord].classList.value).addEventListener("click", event => {
document.querySelector("." + action[cord].classList.value).textContent = "X";
gameState = 1;
});
};
function chasviO(cord) {
document.querySelector("." + action[cord].classList.value).addEventListener("click", event => {
document.querySelector("." + action[cord].classList.value).textContent = "O";
gameState = 0;
});
};
还有项目link还有-https://jsfiddle.net/qwy2k571/
目前每个方框都装满了 "X"。
我知道我不完全理解闭包和范围链,但请给我一个正确的代码以通过示例理解它。
在此先致谢并致以最诚挚的问候。
由于您使用的是 querySelectorAll 它将给出一个集合,您可以迭代它并直接向它添加事件侦听器。另外,您正在添加多个不需要的事件侦听器
var action = document.querySelectorAll('td');
var gameState = 0;
action.forEach((item) => {
item.addEventListener('click', (e) => {
if (gameState === 0) {
e.target.textContent = "X";
gameState = 1;
} else {
e.target.textContent = "0";
gameState = 0;
}
})
})
.main {
padding: 100px 0;
width: 360px;
margin: 0 auto;
}
table,
tbody {
margin: 0;
padding: 0;
width: 360px;
height: 360px;
}
tr {
width: 360px;
height: 120px;
margin: 0;
padding: 0;
}
td {
text-align: center;
width: 120px;
height: 120px;
border: 1px solid #333;
margin: 0;
padding: 0;
font-size: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main">
<table>
<tr>
<td class="b1-1"></td>
<td class="b1-2"></td>
<td class="b1-3"></td>
</tr>
<tr>
<td class="b2-1"></td>
<td class="b2-2"></td>
<td class="b2-3"></td>
</tr>
<tr>
<td class="b3-1"></td>
<td class="b3-2"></td>
<td class="b3-3"></td>
</tr>
</table>
</div>
这里我做了一个可以工作的 jsfiddle:https://jsfiddle.net/1jnkepLg/1/
我将 JS 部分简化为:
var gameState = 0; // Holds the current game state
var cells = document.getElementsByTagName("td"); // list of cells of the game board
// attach an event listener at each cell.
for (var i=0;i<cells.length;i++)
{
cells[i].addEventListener("click", chasvi);
}
function chasvi()
{
// in event callbacks "this" refers to the element that triggered the event. In this case, it is the TD element.
this.textContent = gameState ? "X" : "0";
// switch the game state
gameState = !gameState;
}
这样的话,一个事件监听器就够了,你可以在回调函数中决定绘制什么X|0。
您的错误在于,您在创建事件时仅依赖于 gameState
的当前值,然后再进行任何移动。您需要检查并更改事件中 gameState
的值:
var action = document.querySelectorAll('td');
var gameState = 0;
for (var i = 0; i <= action.length - 1; i++) {
action[i].addEventListener("click", function() {
this.textContent = (gameState = (gameState + 1) % 2) ? 'x' : '0';
})
}
您的解决方案的问题在于,您编写的 for 循环分配事件侦听器以在板上设置 'X' 到每个方块 BEFORE 甚至在板上单击一下。这意味着您的 if 逻辑永远不会分配 'O',因为所有点击都会调用 chasviX()
您应该改用 NodeList
的 forEach()
函数,例如您的 action
变量:
var gameState = false;
action.forEach(function(act) {
act.addEventListener("click", function(event) {
// If gameState is false(logical 0), place an 'X' on the board
// Otherwise, place a 'O'
event.target.textContent = gameState ? 'X' : 'O';
// Invert state at the end
gameState = !gameState;
});
})
在您的第一个循环中:for (var i = 0; i <= action.length - 1; i++) { ... }
您正在向每个单元格添加事件侦听器。由于变量 gameState
被初始化为 0
,条件 if (gameState === 0) { ... }
在该循环中将始终为真。
无需在初始化期间检查游戏状态,只需像您一样向每个单元格添加一个事件侦听器。
为了确保将正确的参数传递给事件,您需要将回调包装在 function() { chasvi(param); }
内,并将整个循环体包装在另一个匿名函数内,将另一个变量设置为 坐标 i
:
for (var i = 0; i <= action.length - 1; i++) {
(function(){
let coor = i;
let getClassName = "." + action[i].classList.value;
document.querySelector(getClassName).addEventListener("click", function() { chasvi(coor); });
}());
}
请注意,我将函数名称更改为 chasvi
,因为您可以在该函数中管理大小写 X
或 O
。
在每个函数 chasviX
和 chasviO
中,您再次添加了一个事件侦听器。这不好,因为每点击一次,您都会再添加一个事件。
点击之前,已经有活动了。
点击一次后,有2个事件。再点击一次,有 3 个事件,依此类推...
我建议您将这些函数更改为 1 个可以处理这两种情况的函数:
function chasvi(cord)
{
let TextToDisplay;
if (gameState === 0)
{
TextToDisplay = "X";
gameState = 1;
}
else
{
TextToDisplay = "O";
gameState = 0;
}
document.querySelector("." + action[cord].classList.value).textContent = TextToDisplay;
}
由于 gameState
只有 2 个状态,您可以使用布尔值。您可以将其初始化为 var gameState = false;
我随机为 X 选择 false。
然后,函数可以简化为:
function chasvi(cord)
{
let TextToDisplay;
if (gameState === false)
{
TextToDisplay = "X";
}
else
{
TextToDisplay = "O";
}
gameState = !gameState; // This swaps the state true/false
document.querySelector("." + action[cord].classList.value).textContent = TextToDisplay;
}
这种写法:
let TextToDisplay;
if (gameState === false)
{
TextToDisplay = "X";
}
else
{
TextToDisplay = "O";
}
可以使用三元表达式进行简化:
let TextToDisplay = gameState ? "O" : "X"; // This means if gameState is true, then "O" is returned, else "X"
最终代码如下所示:
var action = document.querySelectorAll('td');
var gameState = false;
for (var i = 0; i <= action.length - 1; i++) {
(function(){
let coor = i;
getClassName = "." + action[i].classList.value;
document.querySelector(getClassName).addEventListener("click", function() { chasvi(coor); });
}());
}
function chasvi(cord) {
let TextToDisplay = gameState ? "O" : "X";
gameState = !gameState;
document.querySelector("." + action[cord].classList.value).textContent = TextToDisplay;
}
.main {
padding: 100px 0;
width: 360px;
margin: 0 auto;
}
table, tbody {
margin: 0;
padding: 0;
width: 360px;
height: 360px;
}
tr {
width:360px;
height: 120px;
margin: 0;
padding: 0;
}
td {
text-align: center;
width:120px;
height: 120px;
border: 1px solid #333;
margin: 0;
padding: 0;
font-size: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="main">
<table>
<tr>
<td class="b1-1"></td>
<td class="b1-2"></td>
<td class="b1-3"></td>
</tr>
<tr>
<td class="b2-1"></td>
<td class="b2-2"></td>
<td class="b2-3"></td>
</tr>
<tr>
<td class="b3-1"></td>
<td class="b3-2"></td>
<td class="b3-3"></td>
</tr>
</table>
</div>
<script src="script.js" type="text/javascript"></script>
</body>
</html>
我正在努力学习Javascript。
我在 Youtube 上看了一些 Javascript 课程,正在尝试创建我的第一个项目。
项目应该是简单的井字游戏。
功能: 第一次点击框应该填写 "X" 第二次单击另一个框应填充 "Y" 第三次单击另一个框应再次填充 "X",依此类推,直到所有框都被字符填充。
有我的代码 HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="main">
<table>
<tr>
<td class="b1-1"></td>
<td class="b1-2"></td>
<td class="b1-3"></td>
</tr>
<tr>
<td class="b2-1"></td>
<td class="b2-2"></td>
<td class="b2-3"></td>
</tr>
<tr>
<td class="b3-1"></td>
<td class="b3-2"></td>
<td class="b3-3"></td>
</tr>
</table>
</div>
<script src="script.js" type="text/javascript"></script>
</body>
</html>
CSS
.main {
padding: 100px 0;
width: 360px;
margin: 0 auto;
}
table, tbody {
margin: 0;
padding: 0;
width: 360px;
height: 360px;
}
tr {
width:360px;
height: 120px;
margin: 0;
padding: 0;
}
td {
text-align: center;
width:120px;
height: 120px;
border: 1px solid #333;
margin: 0;
padding: 0;
font-size: 50px;
}
Javascript
var action = document.querySelectorAll('td');
var gameState = 0;
for (var i = 0; i <= action.length - 1; i++) {
getClassName = "." + action[i].classList.value;
if (gameState === 0) {
document.querySelector(getClassName).addEventListener("click", chasviX(i));
} else {
document.querySelector(getClassName).addEventListener("click", chasviO(i));
}
}
function chasviX(cord) {
document.querySelector("." + action[cord].classList.value).addEventListener("click", event => {
document.querySelector("." + action[cord].classList.value).textContent = "X";
gameState = 1;
});
};
function chasviO(cord) {
document.querySelector("." + action[cord].classList.value).addEventListener("click", event => {
document.querySelector("." + action[cord].classList.value).textContent = "O";
gameState = 0;
});
};
还有项目link还有-https://jsfiddle.net/qwy2k571/
目前每个方框都装满了 "X"。 我知道我不完全理解闭包和范围链,但请给我一个正确的代码以通过示例理解它。
在此先致谢并致以最诚挚的问候。
由于您使用的是 querySelectorAll 它将给出一个集合,您可以迭代它并直接向它添加事件侦听器。另外,您正在添加多个不需要的事件侦听器
var action = document.querySelectorAll('td');
var gameState = 0;
action.forEach((item) => {
item.addEventListener('click', (e) => {
if (gameState === 0) {
e.target.textContent = "X";
gameState = 1;
} else {
e.target.textContent = "0";
gameState = 0;
}
})
})
.main {
padding: 100px 0;
width: 360px;
margin: 0 auto;
}
table,
tbody {
margin: 0;
padding: 0;
width: 360px;
height: 360px;
}
tr {
width: 360px;
height: 120px;
margin: 0;
padding: 0;
}
td {
text-align: center;
width: 120px;
height: 120px;
border: 1px solid #333;
margin: 0;
padding: 0;
font-size: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main">
<table>
<tr>
<td class="b1-1"></td>
<td class="b1-2"></td>
<td class="b1-3"></td>
</tr>
<tr>
<td class="b2-1"></td>
<td class="b2-2"></td>
<td class="b2-3"></td>
</tr>
<tr>
<td class="b3-1"></td>
<td class="b3-2"></td>
<td class="b3-3"></td>
</tr>
</table>
</div>
这里我做了一个可以工作的 jsfiddle:https://jsfiddle.net/1jnkepLg/1/
我将 JS 部分简化为:
var gameState = 0; // Holds the current game state
var cells = document.getElementsByTagName("td"); // list of cells of the game board
// attach an event listener at each cell.
for (var i=0;i<cells.length;i++)
{
cells[i].addEventListener("click", chasvi);
}
function chasvi()
{
// in event callbacks "this" refers to the element that triggered the event. In this case, it is the TD element.
this.textContent = gameState ? "X" : "0";
// switch the game state
gameState = !gameState;
}
这样的话,一个事件监听器就够了,你可以在回调函数中决定绘制什么X|0。
您的错误在于,您在创建事件时仅依赖于 gameState
的当前值,然后再进行任何移动。您需要检查并更改事件中 gameState
的值:
var action = document.querySelectorAll('td');
var gameState = 0;
for (var i = 0; i <= action.length - 1; i++) {
action[i].addEventListener("click", function() {
this.textContent = (gameState = (gameState + 1) % 2) ? 'x' : '0';
})
}
您的解决方案的问题在于,您编写的 for 循环分配事件侦听器以在板上设置 'X' 到每个方块 BEFORE 甚至在板上单击一下。这意味着您的 if 逻辑永远不会分配 'O',因为所有点击都会调用 chasviX()
您应该改用 NodeList
的 forEach()
函数,例如您的 action
变量:
var gameState = false;
action.forEach(function(act) {
act.addEventListener("click", function(event) {
// If gameState is false(logical 0), place an 'X' on the board
// Otherwise, place a 'O'
event.target.textContent = gameState ? 'X' : 'O';
// Invert state at the end
gameState = !gameState;
});
})
在您的第一个循环中:for (var i = 0; i <= action.length - 1; i++) { ... }
您正在向每个单元格添加事件侦听器。由于变量 gameState
被初始化为 0
,条件 if (gameState === 0) { ... }
在该循环中将始终为真。
无需在初始化期间检查游戏状态,只需像您一样向每个单元格添加一个事件侦听器。
为了确保将正确的参数传递给事件,您需要将回调包装在 function() { chasvi(param); }
内,并将整个循环体包装在另一个匿名函数内,将另一个变量设置为 坐标 i
:
for (var i = 0; i <= action.length - 1; i++) {
(function(){
let coor = i;
let getClassName = "." + action[i].classList.value;
document.querySelector(getClassName).addEventListener("click", function() { chasvi(coor); });
}());
}
请注意,我将函数名称更改为 chasvi
,因为您可以在该函数中管理大小写 X
或 O
。
在每个函数 chasviX
和 chasviO
中,您再次添加了一个事件侦听器。这不好,因为每点击一次,您都会再添加一个事件。
点击之前,已经有活动了。
点击一次后,有2个事件。再点击一次,有 3 个事件,依此类推...
我建议您将这些函数更改为 1 个可以处理这两种情况的函数:
function chasvi(cord)
{
let TextToDisplay;
if (gameState === 0)
{
TextToDisplay = "X";
gameState = 1;
}
else
{
TextToDisplay = "O";
gameState = 0;
}
document.querySelector("." + action[cord].classList.value).textContent = TextToDisplay;
}
由于 gameState
只有 2 个状态,您可以使用布尔值。您可以将其初始化为 var gameState = false;
我随机为 X 选择 false。
然后,函数可以简化为:
function chasvi(cord)
{
let TextToDisplay;
if (gameState === false)
{
TextToDisplay = "X";
}
else
{
TextToDisplay = "O";
}
gameState = !gameState; // This swaps the state true/false
document.querySelector("." + action[cord].classList.value).textContent = TextToDisplay;
}
这种写法:
let TextToDisplay;
if (gameState === false)
{
TextToDisplay = "X";
}
else
{
TextToDisplay = "O";
}
可以使用三元表达式进行简化:
let TextToDisplay = gameState ? "O" : "X"; // This means if gameState is true, then "O" is returned, else "X"
最终代码如下所示:
var action = document.querySelectorAll('td');
var gameState = false;
for (var i = 0; i <= action.length - 1; i++) {
(function(){
let coor = i;
getClassName = "." + action[i].classList.value;
document.querySelector(getClassName).addEventListener("click", function() { chasvi(coor); });
}());
}
function chasvi(cord) {
let TextToDisplay = gameState ? "O" : "X";
gameState = !gameState;
document.querySelector("." + action[cord].classList.value).textContent = TextToDisplay;
}
.main {
padding: 100px 0;
width: 360px;
margin: 0 auto;
}
table, tbody {
margin: 0;
padding: 0;
width: 360px;
height: 360px;
}
tr {
width:360px;
height: 120px;
margin: 0;
padding: 0;
}
td {
text-align: center;
width:120px;
height: 120px;
border: 1px solid #333;
margin: 0;
padding: 0;
font-size: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="main">
<table>
<tr>
<td class="b1-1"></td>
<td class="b1-2"></td>
<td class="b1-3"></td>
</tr>
<tr>
<td class="b2-1"></td>
<td class="b2-2"></td>
<td class="b2-3"></td>
</tr>
<tr>
<td class="b3-1"></td>
<td class="b3-2"></td>
<td class="b3-3"></td>
</tr>
</table>
</div>
<script src="script.js" type="text/javascript"></script>
</body>
</html>