How I Built a Zero-Dependency Wordle Clone with Vanilla JavaScript
When Josh Wardle's Wordle went viral, it sparked an entire ecosystem of spinoffs and niche adaptatio 2026-9-15 13:11:42 Author: hackernoon.com(查看原文) 阅读量:17 收藏

When Josh Wardle's Wordle went viral, it sparked an entire ecosystem of spinoffs and niche adaptations. While the instinct for modern web developers is to spin up create-react-app or a Next.js template for any interactive interface, a 5-letter word puzzle is one of the best use cases for ditching heavy frameworks entirely.

Here is a quick breakdown of how I built a Canadian-themed Wordle clone (Canuckle) using only Vanilla JavaScript, plain HTML5, and CSS3—achieving sub-millisecond initial render times and zero external runtime dependencies.

The Architecture: Zero-Backend State

Word puzzles need persistent state across page reloads without requiring an account or database infrastructure. Everything lives in the browser:

[DOM / Virtual Keyboard]

v

[Game State Engine (Vanilla JS)] <----> [LocalStorage Engine]

v

[Word Validation & Color Match Logic]

The app is built around three core pillars:

  1. Target Word Rotation: A deterministic epoch-based daily selector.
  2. Evaluation Algorithm: Handling exact matches, misplaced letters, and duplicate letter edge cases.
  3. Persistent State Management: Synchronizing tile states, keyboard colors, and streaks with localStorage.

1. Deterministic Daily Word Selection

Instead of calling a backend API each midnight to fetch the "word of the day," the client computes the target word using an offset index based on a fixed launch date:

const WORD_LIST = ["MAPLE", "MOOSE", "CABIN", "CANOE", "POUTN"]; // Curated themed list
const EPOCH_DATE = new Date("2026-01-01T00:00:00").getTime();

function getDailyTargetWord() {
  const now = new Date().getTime();
  const msPerDay = 24 * 60 * 60 * 1000;
  const dayIndex = Math.floor((now - EPOCH_DATE) / msPerDay);
  
  // Modulo ensures list wrap-around without index overflows
  return WORD_LIST[dayIndex % WORD_LIST.length];
}

This guarantees every player across the globe sees the exact same puzzle on any given calendar day without a centralized database.

2. The Duplicate Letter Trap in Word Evaluation

The hardest logic in a Wordle clone is not checking if a letter exists, but handling duplicates.

If the target word is ROBOT and the player guesses FLOOR:

  • The first O is in the wrong spot (Yellow/Present).
  • The second O is in the exact spot (Green/Correct).
  • If you run a naive single-pass includes() check, you will incorrectly highlight both Os even though the target only has two instances, often confusing the counts.

To solve this in pure JavaScript, the algorithm runs in two distinct passes:

function evaluateGuess(guess, target) {
  const targetChars = target.split('');
  const guessChars = guess.split('');
  const result = new Array(5).fill('absent'); // Default: Gray
  const targetCharCounts = {};

  // Count available letters in target
  for (const char of targetChars) {
    targetCharCounts[char] = (targetCharCounts[char] || 0) + 1;
  }

  // Pass 1: Mark exact matches (Correct / Green)
  for (let i = 0; i < 5; i++) {
    if (guessChars[i] === targetChars[i]) {
      result[i] = 'correct';
      targetCharCounts[guessChars[i]]--;
    }
  }

  // Pass 2: Mark misplaced letters (Present / Yellow)
  for (let i = 0; i < 5; i++) {
    if (result[i] !== 'correct') {
      const char = guessChars[i];
      if (targetCharCounts[char] > 0) {
        result[i] = 'present';
        targetCharCounts[char]--;
      }
    }
  }

  return result;
}

3. Lightweight State Serialization

To keep the game resilient against browser refreshes, the state updates to localStorage on every valid row submission:

const gameState = {
  currentStreak: 0,
  guesses: [],
  gameStatus: "IN_PROGRESS", // "WON", "LOST", "IN_PROGRESS"
  lastPlayedDate: new Date().toDateString()
};

function saveState() {
  localStorage.setItem("puzzle_save_v1", JSON.stringify(gameState));
}

function loadState() {
  const saved = localStorage.getItem("puzzle_save_v1");
  if (!saved) return;
  
  const parsed = JSON.parse(saved);
  // Reset board if day has changed
  if (parsed.lastPlayedDate !== new Date().toDateString()) {
    gameState.guesses = [];
    gameState.gameStatus = "IN_PROGRESS";
  } else {
    Object.assign(gameState, parsed);
    rehydrateUI();
  }
}

Why Vanilla JS Still Wins for Utility Projects

Building this without a framework delivered tangible development benefits:

  • Instant Load Times: The total production payload (HTML + CSS + JS) is less than 15 KB uncompressed.
  • No Build Step Friction: Zero npm install, zero Webpack / Vite configuration bugs, and straightforward deployment to any static host.
  • Direct DOM Control: Keyboard layout bindings and flip animations are directly manipulated via classList.add() with zero virtual DOM overhead.

For utility tools and browser games, stepping back from component abstractions and writing plain JavaScript keeps web projects fast, robust, and clean to maintain.


文章来源: https://hackernoon.com/how-i-built-a-zero-dependency-wordle-clone-with-vanilla-javascript?source=rss
如有侵权请联系:admin#unsafe.sh