Изменить глобальную переменную из функции события - PullRequest
2 голосов
/ 19 февраля 2020

Я пытаюсь выучить Javascript.

Я смотрел какой-то курс Javascript на Youtube и пытаюсь создать свой первый проект.

Проект должен быть простым, например c -ta c -toe игра.

Функциональность: первый щелчок на поле должен заполниться буквой «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;
        });
};

Также имеется ссылка на проект - https://jsfiddle.net/qwy2k571/

В данный момент каждая коробка заполняется знаком "X". Я знаю, что не совсем понимаю замыкания и цепочки областей видимости, но, пожалуйста, дайте мне правильный код, чтобы понять его на примере.

Заранее спасибо и наилучшими пожеланиями.

Ответы [ 5 ]

3 голосов
/ 19 февраля 2020

Здесь я сделал рабочий 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 нарисовать в функции обратного вызова.

3 голосов
/ 19 февраля 2020

Поскольку вы используете querySelectorAll, который даст коллекцию, вы можете выполнить итерацию и напрямую добавить к ней прослушиватель событий. Также вы добавляете несколько EventListener, который не требуется

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>
2 голосов
/ 19 февраля 2020

В вашем первом l oop: for (var i = 0; i <= action.length - 1; i++) { ... } вы добавляете прослушиватели событий в каждую ячейку. Поскольку переменная gameState инициализируется в 0, условие if (gameState === 0) { ... } всегда будет истинным в этом l oop.

Вместо проверки состояния игры во время инициализации, просто добавьте прослушиватель событий, как и вы, для каждой ячейки.

Чтобы убедиться, что вы передали правильный параметр событию, вам нужно заключить обратный вызов в function() { chasvi(param); }, а все тело l oop внутри другая анонимная функция, устанавливающая другую переменную в координату 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;
}

Поскольку есть только 2 состояния для gameState, вы можете использовать логическое значение. Вы можете инициализировать его как var gameState = false; Я случайным образом выбираю false для X.

И затем функцию можно упростить до:

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>
2 голосов
/ 19 февраля 2020

Проблема с вашим решением состоит в том, что для l oop, который вы написали, назначается прослушиватель событий для установки 'X' на доске для каждого квадрата ДО есть даже один щелчок на доска. Это означает, что если if logi c никогда не будет присваивать 'O', так как все щелчки вызовут chasviX()

. Вместо этого вы должны использовать forEach() функцию NodeList, такую ​​как переменная 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;
    });
})
0 голосов
/ 19 февраля 2020

Ваша ошибка в том, что вы создаете события таким образом, что они зависят только от текущего значения 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';
  })
}

https://jsfiddle.net/fby3g12v/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...