About this snippet

Build a fully functional Tic Tac Toe game with this complete JavaScript snippet. It features a responsive grid layout, automatic win detection, and a clean modern interface that works perfectly on both desktop and mobile devices.

Size
4.2 KB
Uses
HTML, CSS, JavaScript
Loads
Nothing external
Views
543
<div class="game-wrapper">
  <div class="game-info">
    <h1>Tic Tac Toe</h1>
    <div id="statusMessage" class="status">Player X's Turn</div>
  </div>
  <div class="game-board" id="gameBoard">
    <div class="cell" data-index="0"></div>
    <div class="cell" data-index="1"></div>
    <div class="cell" data-index="2"></div>
    <div class="cell" data-index="3"></div>
    <div class="cell" data-index="4"></div>
    <div class="cell" data-index="5"></div>
    <div class="cell" data-index="6"></div>
    <div class="cell" data-index="7"></div>
    <div class="cell" data-index="8"></div>
  </div>
  <button id="restartBtn" class="restart-btn">Restart Game</button>
</div>
.game-wrapper {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 400px;
}
.game-info h1 {
  margin: 0 0 10px 0;
  color: #1f2937;
  font-size: 2rem;
}
.status {
  margin-bottom: 20px;
  color: #6b7280;
  font-size: 1.2rem;
  font-weight: 500;
  text-align: center;
}
.game-board {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3, 100px);
  gap: 10px;
  margin-bottom: 25px;
}
.cell {
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3rem;
  font-weight: 700;
  cursor: pointer;
  color: #374151;
  transition: background 0.2s, transform 0.2s;
}
.cell:hover {
  background: #f9fafb;
  transform: scale(1.02);
}
.cell.x {
  color: #2563eb;
}
.cell.o {
  color: #ef4444;
}
.restart-btn {
  padding: 12px 24px;
  background: #1f2937;
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.2s;
}
.restart-btn:hover {
  background: #000;
}
@media (max-width: 400px) {
  .game-board {
    grid-template-columns: repeat(3, 80px);
    grid-template-rows: repeat(3, 80px);
  }
  .cell {
    font-size: 2.5rem;
  }
}
const statusDisplay = document.getElementById("statusMessage");
const cells = document.querySelectorAll(".cell");
const restartBtn = document.getElementById("restartBtn");

let gameActive = true;
let currentPlayer = "X";
let gameState = ["", "", "", "", "", "", "", "", ""];

const winningConditions = [
  [0, 1, 2], [3, 4, 5], [6, 7, 8],
  [0, 3, 6], [1, 4, 7], [2, 5, 8],
  [0, 4, 8], [2, 4, 6]
];

function handleCellPlayed(clickedCell, clickedCellIndex) {
  gameState[clickedCellIndex] = currentPlayer;
  clickedCell.innerHTML = currentPlayer;
  clickedCell.classList.add(currentPlayer.toLowerCase());
}

function handlePlayerChange() {
  currentPlayer = currentPlayer === "X" ? "O" : "X";
  // Fixed escaping here: \'s instead of \\'s
  statusDisplay.innerHTML = `Player ${currentPlayer}\'s Turn`;
}

function handleResultValidation() {
  let roundWon = false;
  for (let i = 0; i <= 7; i++) {
    const winCondition = winningConditions[i];
    let a = gameState[winCondition[0]];
    let b = gameState[winCondition[1]];
    let c = gameState[winCondition[2]];
    if (a === "" || b === "" || c === "") continue;
    if (a === b && b === c) {
      roundWon = true;
      break;
    }
  }

  if (roundWon) {
    statusDisplay.innerHTML = `Player ${currentPlayer} Wins!`;
    gameActive = false;
    return;
  }

  if (!gameState.includes("")) {
    statusDisplay.innerHTML = "Game ended in a draw!";
    gameActive = false;
    return;
  }

  handlePlayerChange();
}

function handleCellClick(clickedCellEvent) {
  const clickedCell = clickedCellEvent.target;
  const clickedCellIndex = parseInt(clickedCell.getAttribute("data-index"));

  if (gameState[clickedCellIndex] !== "" || !gameActive) return;

  handleCellPlayed(clickedCell, clickedCellIndex);
  handleResultValidation();
}

function handleRestartGame() {
  gameActive = true;
  currentPlayer = "X";
  gameState = ["", "", "", "", "", "", "", "", ""];
  statusDisplay.innerHTML = "Player X\'s Turn";
  cells.forEach(cell => {
    cell.innerHTML = "";
    cell.classList.remove("x", "o");
  });
}

cells.forEach(cell => cell.addEventListener("click", handleCellClick));
restartBtn.addEventListener("click", handleRestartGame);
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save