Как создать Wordle на JavaScript — пошаговое руководство

Как работает Wordle
В Wordle зашифровано секретное слово из пяти букв. У игрока есть шесть попыток, чтобы угадать это слово, вводя разные пятибуквенные слова.
После отправки догадки игра сообщает о совпадениях через цвета:
- жёлтый: буква есть в слове, но стоит в другой позиции;
- зелёный: буква есть и на правильной позиции;
- серый: буквы в слове нет.

Определения в один абзац:
- Тайл (тайл/ячейка): отдельная клетка на доске для одной буквы.
- Ряд: строка из 5 тайлов; всего 6 рядов.
- Секретное слово: слово, которое нужно угадать, хранится в переменной secretWord.
Развёртывание dev-сервера
Код проекта опубликован в репозитории на GitHub и распространяется по лицензии MIT. Если вы хотите посмотреть живой demo, используйте ссылку в репозитории.
Проект использует Vite для быстрого старта. Рекомендуется иметь Yarn (обычно быстрее npm). Выполните в терминале:
yarn create vite
Выберите фреймворк: Vanilla, вариант: JavaScript. Затем установите зависимости:
yarn
Запустите dev-сервер:
yarn devПримечание: если вы предпочитаете npm, можно заменить команды на эквиваленты npm — функционально примеры остаются теми же.
Настройка игры и клавиатуры
Откройте проект в редакторе кода. Очистите main.js и убедитесь, что структура папок соответствует ожидаемой (например, index.html, main.js, style.css и т.д.).

Замените содержимое index.html на каркас ниже (код сохранён без изменений):
JS Wordle
Wordle Clone
Please wait. The Game is loading...
Для стилей возьмите файл style.css из репозитория проекта и вставьте в свой style.css.
Установите Toastify для уведомлений:
yarn add toastify -S
В main.js импортируйте css и Toastify (код сохранён без изменений):
import "./style.css"
import Toastify from 'toastify-js'
Определите основные переменные для удобной работы с DOM (код сохранён без изменений):
let board = document.querySelector("#board");
let message = document.querySelector("#message");
let keys = "QWERTYUIOPASDFGHJKLZXCVBNM".split("");
let restartBtn = document.querySelector("#restart-btn");
let showBtn = document.querySelector("#show-btn");
showBtn.setAttribute("disabled", "true");
keys.push("Backspace");
let keyboard = document.querySelector(".keyboard");
Важно: HTML в проекте оставлен на английском, чтобы код совпадал с примерами. Вы можете локализовать текст кнопок и сообщений, но тогда убедитесь, что селекторы и data-атрибуты корректно соответствуют обработчикам событий.
Настройка доски игры
Так как Wordle использует 6 попыток по 5 букв, объявим boardContent — массив из шести массивов. Также объявим currentRow и currentBox для навигации по доске (код сохранён без изменений):
let boardContent = [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
];
let currentRow = 0;
let currentBox = 0;
let secretWord;
Для рендеринга доски используйте вложенные циклы, создавая по 5 span-элементов в каждом ряду (код сохранён без изменений):
for (let i = 0; i <= 5; i++) {
let row = document.createElement('div')
for (let y = 0; y <= 4; y++) {
let box = document.createElement('span');
row.appendChild(box);
row.className = `row-${i + 1}`
}
board.appendChild(row);
}Совет UX: добавьте aria-атрибуты и tabindex для тайлов, если планируете поддерживать клавиатурную навигацию и вспомогательные технологии.
Добавление клавиатуры и обработка ввода
Клавиатура создаётся простым перебором keys: для каждой записи создаём кнопку, присваиваем ей класс key и data-key, навешиваем клик, который вызывает insertKey. (код сохранён без изменений):
keys.forEach(entry => {
let key = document.createElement("button");
if (entry === "*") {
key.innerText = "Backspace";
} else {
key.innerText = entry;
}
key.className = "key";
key.setAttribute("data-key", entry.toUpperCase());
key.addEventListener("click", () => {
insertKey(entry.toUpperCase())
setTimeout(() => {
document.querySelector(`button[data-key=${entry.toUpperCase()}]`).blur();
}, 250)
})
keyboard.append(key);
})
Обратите внимание: в различных раскладках клавиатур символы и регистр могут отличаться. Функция isValidCharacter (ниже) решает, какие символы считаются допустимыми.
Получение слова из API
При загрузке игра получает случайное пятибуквенное слово из Random Word API и сохраняет его в secretWord (код сохранён без изменений):
function getNewWord() {
async function fetchWord() {
try {
const response = await fetch("https://random-word-api.herokuapp.com/word?length=5");
if (response.ok) {
const data = await response.json();
return data;
} else {
throw new Error("Something went wrong!")
}
} catch (error) {
message.innerText = `Something went wrong. \n${error}\nCheck your internet connection.`;
}
}
fetchWord().then(data => {
secretWord = data[0].toUpperCase();
main();
})
}Важно: публичные API могут возвращать неподходящие слова (редкие или устаревшие). Для продакшена рассмотрите собственный словарь с контролем допустимых слов.
Основная функция и рендеринг
Функция main запускается после успешного получения слова. Её задача — подготовить список всех тайлов, скрыть сообщение загрузки и навесить глобальный обработчик клавиатуры (фрагменты кода сохранены без изменений):
function main(){
}
Внутри main соберите все row-элементы в один массив boxes, добавьте класс empty для непустых тайлов и скройте сообщение загрузки (код сохранён без изменений):
rows.forEach(row => [...row.children].forEach(child => boxes.push(child)))
boxes.forEach((box) => {
box.classList.add("empty");
})
message.style.display = "none";
Добавьте обработчик keyup на window: если пользователь отпустил допустимую клавишу, найдите соответствующую кнопку, сфокусируйте её, сымитируйте клик и снимите фокус через 250 мс (код сохранён без изменений):
window.addEventListener('keyup', (e) => {
if (isValidCharacter(e.key)) {
document.querySelector(`button[data-key=${e.key.toUpperCase()}]`).focus();
document.querySelector(`button[data-key=${e.key.toUpperCase()}]`).click();
setTimeout(() => {
document.querySelector(`button[data-key=${e.key.toUpperCase()}]`).blur();
}, 250)
}
})
После этого добавьте обработчики на кнопки showBtn и restartBtn, а также функцию isValidCharacter (код сохранён без изменений):
showBtn.addEventListener('click', () => {
Toastify({
text: `Alright fine! the answer is ${secretWord}`,
duration: 2500,
className: "alert",
}).showToast();
})
restartBtn.addEventListener('click', () => {
location.reload();
})
function isValidCharacter(val) {
return (val.match(/^[a-zA-Z]+$/) && (val.length === 1 || val === "Backspace"))
}
Совет: для поддержки локализации можно хранить ключевые тексты (например, сообщения и надписи кнопок) в отдельном объекте переводов.
Рендеринг отдельной ячейки
Функция renderBox обновляет текст в конкретной ячейке (код сохранён без изменений):
function renderBox(row, box, data) {
[...document.querySelector(`.row-${row}`).children][box].innerText = data;
}
Обработка нажатий клавиш и вставка символов
Функция insertKey обновляет boardContent, рендерит буквы в текущую ячейку и переводит ход на следующий ряд, если ряд заполнен (код сохранён без изменений):
function insertKey(key) {
if (key === "Backspace".toUpperCase() && currentRow < boardContent.length) {
boardContent[currentRow][currentBox] = 0;
if (currentBox !== 0) {
currentBox--;
renderBox(currentRow + 1, currentBox, "");
}
} else {
if (currentRow < boardContent.length) {
boardContent[currentRow][currentBox] = key;
renderBox(currentRow + 1, currentBox, key);
currentBox++;
}
if (currentRow < boardContent.length && boardContent[currentRow][currentBox] !== 0) {
evaluate(currentRow, key);
currentBox = 0;
currentRow++;
}
}
}Замечание: логика переключения currentBox/currentRow предполагает, что проверка заполнения ряда происходит после записи последней буквы. При редактировании алгоритма будьте осторожны с индексами, чтобы не выходить за границы массива.
Оценка догадки игрока
Функция evaluate принимает номер ряда и вычисляет цвета для каждой буквы: зелёный — правильная буква на месте, жёлтый — буква есть в слове, но не на месте, серый — буквы нет. Также начиная с четвёртой попытки показывается кнопка “Show Answer” (код сохранён без изменений):
function evaluate(row){
}Внутри найдём догадку и массив для сравнения (код сохранён без изменений):
if (currentRow === 4) {
showBtn.removeAttribute('disabled')
}
let guess = boardContent[row].join('').toUpperCase();
let answer = secretWord.split("");
Алгоритм подсветки реализован через два прохода: сначала помечаем точные совпадения, затем ищем оставшиеся буквы для жёлтого/серого статуса (код сохранён без изменений):
let colors = guess
.split("")
.map((letter, idx) => letter == answer[idx] ? (answer[idx] = false) : letter)
.map((letter, idx) =>
letter
? (idx = answer.indexOf(letter)) < 0
? "grey"
: (answer[idx] = "yellow")
: "green"
);
Затем применяем цвета к тайлам и кнопкам клавиатуры с помощью setColor (код сохранён без изменений):
function setColor(colors) {
colors.forEach((color, index) => {
document.querySelector(`button[data-key=${guess[index].toUpperCase()}]`).style.backgroundColor = color;
document.querySelector(`button[data-key=${guess[index].toUpperCase()}]`).style.color= "black";
[...document.querySelector(`.row-${row + 1}`).children][index].style.backgroundColor = color;
})
}
Замечание по алгоритму: метод, использованный здесь, мутирует массив answer, чтобы учесть повторяющиеся буквы корректно. Это типичный подход для Wordle-подобных игр.
Завершение и запуск
Чтобы начать игру, вызовите getNewWord(); (код сохранён без изменений):
getNewWord();
Поздравляем — вы воссоздали базовую версию Wordle на JavaScript!

Советы по улучшению и альтернативные подходы
Ниже — практические рекомендации, расширения и чеклисты, которые помогут сделать проект надёжнее, удобнее и более зрелым.
Альтернативные подходы к получению слов
- Локальный словарь: храните список допустимых слов в JSON и выбирайте случайный элемент. Плюсы: независимость от сети, контроль качества слов. Минусы: больший объём репозитория.
- Серверная логика: храните словарь и API на собственном сервере, добавьте авторизацию, статистику и модерацию.
- Комбинация: сначала пытаться получить слово из удалённого API, при неудаче — брать из локального словаря.
Когда не годится случайное API: в офлайн-приложениях, при строгой цензуре слов или когда нужен контроль частоты появления слов.
Модели мышления и эвристики при разработке
- Разделяйте UI и логику: оставляйте алгоритм оценки догадок вне DOM-операций для простого тестирования.
- Предположение о повторяющихся буквах: сначала помечайте зелёные совпадения, затем разбирайтесь с оставшимися буквами.
- Минимизируйте прямые селекторы: используйте классы и data-атрибуты для тестируемости.
Мини-методология разработки (быстрый план)
- Подготовка: scaffold Vite + пустые файлы.
- Разметка: index.html — доска, клавиатура, контролы.
- Рендеринг: динамически создаём DOM-тайлы и клавиши.
- Логика: boardContent, insertKey, renderBox.
- Оценка: evaluate + setColor.
- Тесты: ручные и автоматизированные (unit-тесты для evaluate).
- Улучшения: локализация, словарь, статистика, анимации.
Роль‑ориентированные чеклисты
Для frontend‑разработчика:
- Развернут проект через Vite
- Рендеринг доски работает (6 рядов × 5 ячеек)
- Keyboard генерируется динамически
- Обработчики кликов и keyup корректны
- Нет утечек памяти при навешивании слушателей
Для тестировщика:
- Проверить оценку для слов с повторяющимися буквами
- Проверить работу Backspace на граничных индексах
- Проверить, что Show Answer активируется на 5‑й попытке
- Проверить корректность подсветки клавиатуры
Для дизайнера:
- Контраст цветов соответствует доступности
- Тайлы и кнопки имеют фокусные состояния
- Анимации не мешают восприятию
Тестовые случаи и критерии приёмки
- TC1: Ввод корректного пятибуквенного слова — каждая буква отображается в правильном тайле.
- TC2: При совпадении буквы и позиции — тайл и соответствующая клавиша окрашиваются в зелёный.
- TC3: При совпадении буквы, но неверной позиции — жёлтый.
- TC4: Повторяющиеся буквы в догадке и ответе — обработка по правилам Wordle (ограничиваем количество жёлтых/зелёных в соответствии с частотой в answer).
- TC5: Нажатие Backspace удаляет предыдущую букву и не вызывает сдвиг рядов.
- Критерии приёмки: все TC должны пройти, нет выбросов в консоли, UX‑сообщения понятны.
Отладка и отлов ошибок
- Если тайлы не заполняются — проверьте индексы currentBox/currentRow.
- Если showBtn не активируется — убедитесь, что currentRow проверяется на значение 4 до инкремента.
- Если fetch возвращает null/ошибку — проверяйте CORS и доступность внешнего API.
Производительность и безопасность
- Для крупных словарей загружайте словарь лениво (lazy load) или используйте бинарный формат.
- Не храните приватные ключи в клиентском коде.
- Обрабатывайте ошибки fetch и предоставляйте fallback (локальный словарь).
Локализация и адаптация для России
- Словарь: для русскоязычной версии используйте список русских слов длиной 5 букв. Учитывайте кириллицу при проверке isValidCharacter (/^[а-яА-ЯёЁ]+$/).
- Интерфейс: переведите текст кнопок и подсказок; при этом data-key лучше оставить в верхнем регистре соответствующей раскладки.
- Формат даты и текста: используйте нейтральные формулировки, понятные локальной аудитории.
Примеры улучшений UX
- Добавить анимацию переворачивания тайла при оценке (как в оригинальном Wordle).
- Сохранять текущую игру в localStorage, чтобы пользователь мог продолжить.
- Добавить статистику (победы, серии), подсказки и ограничения на словарь.
Decision tree: игровой цикл (Mermaid)
flowchart TD
Start'Запуск' --> Fetch{Получено слово?}
Fetch -- Да --> RenderBoard'Рендер доски'
Fetch -- Нет --> Error[Показать ошибку и fallback]
RenderBoard --> Input[Ожидание ввода]
Input --> Update[insertKey обновляет доску]
Update --> FullRow{Ряд заполнен?}
FullRow -- Да --> Eval[evaluate + setColor]
FullRow -- Нет --> Input
Eval --> Win{Слово угадано?}
Win -- Да --> End[Победа — показать диалог]
Win -- Нет --> Attempts{Попытки закончились?}
Attempts -- Нет --> Input
Attempts -- Да --> Reveal[Показать ответ]Короткое объявление для соцсетей (100–200 слов)
Создал простой клон Wordle на чистом JavaScript с Vite и Toastify. В репозитории есть весь код: рендеринг доски, виртуальная клавиатура, получение случайного слова через API и алгоритм подсветки букв. Подойдёт как учебный проект для начинающих: учит DOM‑манипуляциям, обработке событий и работе с fetch. В статье — чеклисты для тестировщиков, советы по локализации и идеи для улучшений (локальный словарь, локализация для кириллицы, хранение прогресса в localStorage). Ссылка на демо и инструкции по быстрому запуску — в репозитории.
Итоги
Важно: сохраняйте логику оценки игровых правил отдельно от DOM‑кода — это облегчает тестирование и переносимость на другие платформы (React, Svelte, сервер). Для продакшена подумайте о локальном словаре, тестах на повторяющиеся буквы и поддержке нескольких раскладок клавиатуры.
Ключевые шаги для старта: scaffold Vite → подключить Toastify → сгенерировать DOM для доски и клавиатуры → реализовать insertKey → реализовать evaluate → получить слово из API или локального словаря → запустить getNewWord().
Важно: если вы собираетесь локализовать игру на русский язык, замените проверки символов и словарь на набор слов на кириллице и протестируйте обработку ё/е.
Счастливого кодинга — и удачи в угадывании секретного слова!
Похожие материалы
Gmail и настольные клиенты: выбор и настройка
SketchUp бесплатно: как начать 3D‑моделирование
Как создать аккаунт PlayStation Network (PSN)
Почему iPhone и iPad нагреваются и как это исправить
Как искать жильё на Airbnb для отпуска