<div class="container">
      <div class="board" id="board">
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
        <div class="cell" data-cell></div>
      </div>
      <div class="text-center mt-3">
        <h3 id="winnner"></h3>
      </div>
      <div class="text-center mt-3">
        <button id="restartButton" class="btn btn-primary">Restart Game</button>
      </div>
    </div>

    <!-- results  -->
    <div
      class="modal fade"
      id="resultModal"
      tabindex="-1"
      aria-labelledby="resultModalLabel"
      aria-hidden="true"
    >
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="resultModalLabel">Game Over</h5>
            <button
              type="button"
              class="close"
              data-dismiss="modal"
              aria-label="Close"
            >
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body" id="results"></div>
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-primary"
              data-dismiss="modal"
              id="playBtn"
            >
              Play Again
            </button>
          </div>
        </div>
      </div>
    </div>
.board {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  max-width: 300px;
  margin: 50px auto;
}
.cell {
  width: 100px;
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  cursor: pointer;
  border: 1px solid #000;
}

.x::before {
  content: "X";
  color: blue;
  position: absolute;
}
.circle::before {
  content: "O";
  color: red;
  position: absolute;
}
.board.hover-x [data-cell]:hover:not(.x):not(.circle) {
  background-color: lightblue;
}

.board.hover-circle [data-cell]:hover:not(.x):not(.circle) {
  background-color: lightcoral;
}
const X_CLASS = "x";
const CIRCLE_CLASS = "circle";

const HOVER_X_CLASS = "hover-x";
const HOVER_CIRCLE_CLASS = "hover-circle";
const WINNING_COMBINATIONS = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],
  [0, 4, 8],
  [2, 4, 6]
];

const cellElements = document.querySelectorAll("[data-cell]");

const board = document.getElementById("board");
const restartButton = document.getElementById("restartButton");
const winner = document.getElementById("winnner");
const resultMessage = document.getElementById("results");

const closeButton = document.querySelector(".modal .close");

let circleTurn;

startGame();

function startGame() {
  circleTurn = false;
  cellElements.forEach((cell) => {
    cell.classList.remove(X_CLASS);
    cell.classList.remove(CIRCLE_CLASS);
    cell.removeEventListener("click", handleClick);
    cell.addEventListener("click", handleClick, { once: true });
  });
}

function handleClick(e) {
  const cell = e.target;
  const currentClass = circleTurn ? CIRCLE_CLASS : X_CLASS;
  console.log(currentClass);

  cell.classList.add(currentClass);
  circleTurn = !circleTurn;

  if (checkWin(currentClass)) {
    showResult(`${currentClass.toUpperCase()} wins`);
  } else if (isDraw()) {
    showResult(`It's a Draw`);
  } else {
    setBoardHoverClass();
  }
}

function setBoardHoverClass() {
  board.classList.remove(HOVER_X_CLASS);
  board.classList.remove(HOVER_CIRCLE_CLASS);
  if (circleTurn) {
    board.classList.add(HOVER_CIRCLE_CLASS);
  } else {
    board.classList.add(HOVER_X_CLASS);
  }
}
function checkWin(currentClass) {
  return WINNING_COMBINATIONS.some((combination) => {
    return combination.every((index) => {
      return cellElements[index].classList.contains(currentClass);
    });
  });
}
function isDraw() {
  return [...cellElements].every((cell) => {
    return (
      cell.classList.contains(X_CLASS) || cell.classList.contains(CIRCLE_CLASS)
    );
  });
}

function showResult(message) {
  resultMessage.textContent = message;
  resultModal.show();
}

restartButton.addEventListener("click", startGame);
document.getElementById("playBtn").addEventListener("click", startGame);
const resultModal = new bootstrap.Modal(
  document.getElementById("resultModal"),
  {
    keyboard: false
  }
);

closeButton.addEventListener("click", () => {
  resultModal.hide();
  startGame();
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
  2. https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js
  3. https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js