LoginSignup
0
0

More than 3 years have passed since last update.

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

Posted at

前回作った三目並べを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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0