Ich versuche Javascript zu lernen.

Ich habe einen Javascript-Kurs auf Youtube gesehen und versuche, mein erstes Projekt zu erstellen.

Das Projekt sollte ein einfaches Tic-Tac-Toe-Spiel sein.

Funktionalität: Der erste Klick auf ein Feld sollte mit "X" gefüllt sein. Der zweite Klick auf ein anderes Feld sollte mit "Y" gefüllt sein. Der dritte Klick auf ein anderes Feld sollte wieder mit "X" gefüllt sein und so weiter, bis alle Felder mit Zeichen gefüllt sind.

Da sind meine Codes 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;
        });
};

Es gibt auch einen Projektlink - https://jsfiddle.net/qwy2k571/

Im Moment füllt sich jede Box mit "X". Ich weiß, dass ich Verschlüsse und Scope-Ketten nicht genau verstanden habe, aber bitte geben Sie mir einen korrekten Code, um ihn anhand eines Beispiels zu verstehen.

Danke im Voraus und beste Grüße.

2
Levan Sakvarelidze 19 Feb. 2020 im 16:17

5 Antworten

Beste Antwort

In Ihrer ersten Schleife: for (var i = 0; i <= action.length - 1; i++) { ... } fügen Sie jeder Zelle Ereignis-Listener hinzu. Da die Variable gameState mit 0 initialisiert wird, ist die Bedingung if (gameState === 0) { ... } innerhalb dieser Schleife immer wahr.

Anstatt den Status des Spiels während der Initialisierung zu überprüfen, fügen Sie jeder Zelle einfach einen Ereignis-Listener hinzu, wie Sie es getan haben.

Um sicherzustellen, dass Sie den richtigen Parameter an das Ereignis übergeben, müssen Sie den Rückruf in function() { chasvi(param); } und den gesamten Körper der Schleife in eine andere anonyme Funktion einschließen und eine andere Variable auf die -Koordinate setzen 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); });
    }());
}

Beachten Sie, dass ich den Namen der Funktion in chasvi geändert habe, da Sie den Fall X oder O in dieser Funktion verwalten können.


In jeder der Funktionen chasviX und chasviO fügen Sie erneut einen Ereignis-Listener hinzu. Dies ist nicht gut, da Sie bei jedem Klick ein weiteres Ereignis hinzufügen.

Vor dem Klicken gibt es bereits ein Ereignis.

Nach einmaligem Klicken gibt es 2 Ereignisse. Noch ein Klick und es gibt 3 Ereignisse und so weiter ...

Ich schlage vor, diese Funktionen in eine Funktion zu ändern, die beide Fälle behandelt:

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;
}

Da es für gameState nur zwei Zustände gibt, können Sie einen booleschen Wert verwenden. Sie können es mit var gameState = false; initialisieren. Ich wähle zufällig false für das X.

Und dann kann die Funktion vereinfacht werden, um:

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;
}

Diese Art der Notation:

let TextToDisplay;

if (gameState === false)
{
    TextToDisplay = "X";
}
else
{
    TextToDisplay = "O";
}

Kann mit ternären Ausdrücken vereinfacht werden:

let TextToDisplay = gameState ? "O" : "X"; // This means if gameState is true, then "O" is returned, else "X"

Der endgültige Code kann folgendermaßen aussehen:

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
Cid 19 Feb. 2020 im 13:57

Das Problem bei Ihrer Lösung besteht darin, dass die von Ihnen geschriebene for-Schleife Ereignis-Listener zum Setzen eines 'X' auf der Tafel für jedes einzelne Quadrat zuweist, BEVOR es sogar einen einzigen Klick auf die Tafel gibt. Dies bedeutet, dass Ihre if-Logik niemals ein 'O' zuweist, da alle Klicks chasviX() aufrufen.

Sie sollten stattdessen eine forEach() -Funktion einer NodeList wie Ihre action -Variable verwenden:

 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;
    });
})
2
TKD21 19 Feb. 2020 im 13:49

Ihr Fehler ist, dass Sie die Ereignisse so erstellen, dass sie nur vom aktuellen Wert von gameState abhängen, bevor Züge ausgeführt wurden. Sie müssen den Wert von gameState innerhalb des Ereignisses überprüfen und ändern:

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/

0
Lajos Arpad 19 Feb. 2020 im 13:45

Hier habe ich eine funktionierende jsfiddle erstellt: https://jsfiddle.net/1jnkepLg/1/

Ich habe den JS-Teil vereinfacht, um:

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;

}

In diesem Fall reicht ein Ereignis-Listener aus. Sie können bestimmen, welches X | 0 bei der Rückruffunktion gezeichnet werden soll.

3
Eriks Klotins 19 Feb. 2020 im 13:37

Da Sie querySelectorAll verwenden, das eine Sammlung ergibt, können Sie diese iterieren und direkt einen Ereignis-Listener hinzufügen. Außerdem fügen Sie mehrere Ereignislistener hinzu, die nicht erforderlich sind

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>
3
brk 19 Feb. 2020 im 13:28