Help us understand the problem. What is going on with this article?

Reactのチュートリアルの三目並べをjQueryでやる

前回作った三目並べをjQueryで書いてみようと思います。
どれくらい煩雑になるのだろう。

数値の表示のみ

index.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="css/index.css" />
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body>
    <div id="root">
      <div class="game">
        <div class="gmae-board">
          <div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
          </div>
        </div>
        <div class="game-info">
          <div>次の手番: X</div>
          <div>
            <!--<li><button>Go to game start</button></li>-->
            <!-- <li><button>Go to move #1</button></li> -->
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
index.css
body {
  font: 14px 'Century Gothic', Futura, sans-serif;
  margin: 20px;
}

ol,
ul {
  padding-left: 30px;
}

.board-row:after {
  clear: both;
  content: '';
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}

以下処理だけで行けそうです。

index.js
$(function() {
  $(".square").each((index, elem) => {
    $(elem).text(index);
  });
});

Live Serverの拡張をインストールして、以下からブラウザを起動します。
image.png
出来てました!
image.png

XとOを入力できるようにする

基本的な設計はチュートリアルを真似します。

index.js
$(function() {
  let xIsNext = true;

  $(".square").click(function() {
    $(this).text(xIsNext ? "X" : "O");
    xIsNext = !xIsNext;
  });
});

image.png

履歴なしの完成までもっていく

index.js
//import $ from "jQuery";

$(function() {
  const squares = new Array(9).fill(null);
  let xIsNext = true;
  const status = $(".game-info div:first-child");

  $(".square").click(function() {
    // 押されたsquareのインデックス
    const index = $(".square").index(this);

    if (calculateWinner(squares) || squares[index]) {
      return;
    }

    squares[index] = xIsNext ? "X" : "O";
    $(this).text(squares[index]);
    status.text("次の手番: " + (xIsNext ? "O" : "X"));
    xIsNext = !xIsNext;
  });

  // 勝敗判定関数(公式チュートリアルから拝借)
  function calculateWinner(squares) {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];
    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (
        squares[a] &&
        squares[a] === squares[b] &&
        squares[a] === squares[c]
      ) {
        return squares[a];
      }
    }
    return null;
  }
});

ダウンロード (2).gif

一旦完成

ここまでだと、Reactよりソースコード短いかな?

履歴機能を持たせる

履歴機能を持たせて、最後まで作ります。

index.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="css/index.css" />
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body>
    <div id="root">
      <div class="game">
        <div class="gmae-board">
          <div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
          </div>
        </div>
        <div class="game-info">
          <div>次の手番: X</div>
          <div>
            <li><button>Go to game start</button></li>
            <!-- <li><button>Go to move #1</button></li> -->
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

jsは信じられないくらいワケわかめになりました。
もっとスマートにしたかった・・・。
(行数長かったんでコメント右に書いてます)

index.js
//import $ from "jQuery";

$(function() {
  // 履歴
  const history = [{ squares: new Array(9).fill(null) }];

  let stepNumber = 0; // 現在表示している履歴のインデックスを表す
  const status = $(".game-info div:first-child"); // 手番
  const allJqSquare = $(".square"); // 全square jQueryオブジェクト

  // 最初の履歴ボタン
  const startButton = $(".game-info li")
    .find("button:first")
    .click(historyButtonClick.bind(null, 0)); // bindで引数を縛る

  // square押下処理
  allJqSquare.click(function() {
    const index = allJqSquare.index(this); // 押されたsquareのインデックス
    const current = history[stepNumber]; // 現在のsquares
    const squares = current.squares;

    if (calculateWinner(squares) || squares[index]) {
      return;
    }

    const newSquares = squares.concat(); // squaresのコピー
    newSquares[index] = stepNumber % 2 === 0 ? "X" : "O";
    $(this).text(newSquares[index]);
    status.text("次の手番: " + (stepNumber % 2 === 0 ? "O" : "X"));

    // カレントのstepNumberより後ろのボタンを削除する
    $(".game-info li").each((i, element) => {
      if (i > stepNumber) {
        $(element).remove();
      }
    });

    // 現在のステップまで履歴も削除
    for (; stepNumber + 1 < history.length; ) {
      history.pop();
    }

    history.push({ squares: newSquares }); // 新しい要素追加
    stepNumber++;

    // 新たに履歴ボタンを作る
    // 親のliごとクローンする
    startButton
      .parent()
      .clone()
      .appendTo(startButton.parent().parent()) // .game-infoに追加
      .children("button")
      .text("Go to move #" + stepNumber) // ボタン名変更
      .click(historyButtonClick.bind(null, stepNumber)); // bindで引数を縛る
  });

  // 履歴ボタン押下処理
  function historyButtonClick(sNumber) {
    stepNumber = sNumber;
    status.text("次の手番: " + (stepNumber % 2 === 0 ? "X" : "O")); // ここは上とは反対になる

    // 全squareのテキストを書き直す
    history[sNumber].squares.forEach((value, i) => {
      allJqSquare.eq(i).text(value);
    });
  }

  // 勝敗判定関数(公式チュートリアルから拝借)
  function calculateWinner(squares) {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];
    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (
        squares[a] &&
        squares[a] === squares[b] &&
        squares[a] === squares[c]
      ) {
        return squares[a];
      }
    }
    return null;
  }
});

感想

履歴がないバージョンまではスラスラできたんですが、履歴のとこで死にました。

私のやり方が悪いのもあるかもしれませんが、Reactのメリットを実感しました!tbn

GodPhwng
業界は7年程度です。 なーなーに理解していたところをしっかりと正しく理解したいです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした