Kanji 777 Trainer

by Bret Lowrey

HTML

<body>
  <div class="content" id="content-display">
    <div class="kanji" id="kanji-display"></div>
    <div class="caption" id="caption-display"></div>
  </div>

  <div class="search-container">
    <input
      type="text"
      id="search-box"
      class="search-box"
      placeholder="Search Kanji"
    />
    <button class="kanji-button" onclick="searchKanji()">Search</button>
    <button class="kanji-button" onclick="displayRandomKanji()">Random</button>
  </div>

  <div class="history" id="history-display"></div>
</body>

CSS

body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 90vh;
  margin: 5px;
  font-family: Arial, sans-serif;
  background-color: #f0f0f0;
}
.kanji {
  font-size: 15vw;
  text-align: center;
}
.caption {
  font-size: 1rem;
  text-align: center;
  margin-top: 5px;
  max-width: 90vw;
}
.content {
  border: 10px solid black;
  border-radius: 20px; /* Rounded corners */
  padding: 5vh;
  background-color: white; /* Background color for better contrast */
  min-width: 40vw;
  min-height: 20vh;
  max-width: 95vw;
  max-height: fit-content;
}
.history {
  /* color: #f0f0f0; */
  color: #a9a9a9;
  font-size: 0.5em;
  margin-top: 20px;
}
.search-container {
  margin-top: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.search-box {
  padding: 10px;
  font-size: 1rem;
  width: 200px;
  border: 2px solid #000;
  border-radius: 5px;
}
.kanji-button {
  padding: 10px;
  font-size: 1rem;
  margin-left: 10px;
  cursor: pointer;
  background-color: #000;
  color: #fff;
  border: none;
  border-radius: 5px;
}
@keyframes jiggle {
  0% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-5px);
  }
  50% {
    transform: translateX(5px);
  }
  75% {
    transform: translateX(-5px);
  }
  100% {
    transform: translateX(0);
  }
}
.jiggle {
  animation: jiggle 0.3s ease;
}

JavaScript

const captionDisplay = document.getElementById("caption-display")
const kanjiDisplay = document.getElementById("kanji-display")
const historyDisplay = document.getElementById("history-display")
const contentDisplay = document.getElementById("content-display")
const searchBox = document.getElementById("search-box")
const kanji = kanjiDictionary()
const kanjiData = {}
for (const k of kanji) {
  kanjiData[k.kanji] = k
}
const history = []

function displayKanji(kanjiInfo) {
  const { kanji, meanings, hiragana, romanji } = kanjiInfo
  captionDisplay.innerHTML = "<div>Click to display</div>"
  kanjiDisplay.textContent = kanji
  contentDisplay.addEventListener("click", function (event) {
    event.stopPropagation()

    captionDisplay.innerHTML = `
            <div>
              <span><b>Meanings</b>: <i>${meanings}</i> | </span>
              <span><b>Hiragana:</b> <i>${hiragana}</i> | </span>
              <span><b>Romanji:</b> <i>${romanji}</i></span>
            </div>
          `.trim()
  })
  history.push(kanjiInfo.kanji)
  historyDisplay.innerHTML = `${history.join(", ")}`
}

function displayRandomKanji() {
  const keys = Object.keys(kanjiData)
  const randomKey = keys[Math.floor(Math.random() * keys.length)]
  displayKanji(kanjiData[randomKey])
}

function findKanji(kanjiData, query) {
  if (kanjiData[query]) {
    return kanjiData[query]
  }
  for (const entry of Object.values(kanjiData)) {
    for (const meaning of entry.meanings) {
      if (meaning.startsWith(query.toLowerCase())) {
        return entry
      }
    }
    if (entry.hiragana === query) {
      return entry
    }
    if (entry.romanji === query) {
      return entry
    }
  }
  return undefined
}

function searchKanji() {
  const searchQuery = searchBox.value.trim()
  const result = findKanji(kanjiData, searchQuery)
  if (result) {
    displayKanji(result)
  } else {
    // Add jiggle effect on not found
    searchBox.classList.add("jiggle")
    setTimeout(() =>...