Некорректный перехват нажатия клавиши в JS

в своей программе я хочу, чтобы когда пользователь ввел необходимый текст вызвать функцию alert. Все происходит правильно, только я вынужден набрать на 1 символ больше. Например, вместо того, чтобы просто набрать ЗДОРОВА, я должен дополнительно нажать еще на 1 клавишу (любую). Вот код:

let textBlock = document.getElementById("textBlock");
let textPlace = document.getElementById("textPlace");


function keyHandler(e) {
    let len = textPlace.value.length;
    if (textPlace.value === textBlock.textContent) {
        alert("Ты победил!");
    }
    else if (textPlace.value != textBlock.textContent.slice(0, len)){
        e.target.style.backgroundColor = "red";
    }   
    else {
        e.target.style.backgroundColor = "white";
    }
    
}

textPlace.addEventListener("keydown", keyHandler);

Ответы (3 шт):

Автор решения: Сергей Мишин

Сделайте вместо keydown обработку на отпускание клавиши keyup.

Потому что в ином случае событие срабатывает, но на момент его срабатывания в текстовом поле ещё ничего нет. Просто отработка события, ничего более.

→ Ссылка
Автор решения: vsemozhebuty

Когда происходит событее 'keydown', содержимое поля ещё не изменилось, новый символ ещё не вставлен. Попробуйте использовать событие 'keyup', а ещё лучше событие 'input' (тогда будет работать, например, и при вставке из буфера без участия клавиатуры).

→ Ссылка
Автор решения: DiD

Если вы хотите использовать keydown, знайте, он срабатывает до того, как <input> успеет обработать нажатие клавиши (кстати, это нажатие можно еще и отменить так, что до <input> вообще не дойдет никакой информации).

Но вы можете приплюсовать букву последней клавиши к результату сами.

И ещё я бы рекомендовал заменить работающий везде alert() на что-то более современное (глючащее во многих браузерах) типа <dialog>.

let textBlock = document.getElementById("textBlock");
let textPlace = document.getElementById("textPlace");


function keyHandler(e) {
  console.clear();
  console.log(`Была нажата клавиша "${e.key}", каретка в позиции ${e.target.selectionStart}`);
  const value = textPlace.value;
  const caret = e.target.selectionStart;
  // новоеЗначение = значениеДоКаретки + буква + значениеПослеКаретки
  const newValue = value.substr(0,caret)+e.key+value.substr(caret);
  let len = value.length;
  if (newValue.trim() === textBlock.textContent.trim()) {
    window.requestAnimationFrame(() => {
      // А вот тут, мои дорогие, значение <input> будет уже с буквой
      // И если сделать проверку здесь, то можно не заморачиваться 
      // ручной вставкой буквы в значение <input>
      console.log('значение в input :', textPlace.value);
      if(textPlace.value.trim() === textBlock.textContent.trim()){
        openDialog();
      }
    });
  } else if (textPlace.value != textBlock.textContent.slice(0, len)) {
    e.target.style.backgroundColor = "red";
  } else {
    e.target.style.backgroundColor = "white";
  }

}

function openDialog() {
  let dialog = document.querySelector('#win-dialog');
  if (dialog.showModal) {
    dialog.showModal();
  } else {
    dialog.show();
  }
}

textPlace.addEventListener("keydown", keyHandler);
* {
  box-sizing: border-box;
}

#textBlock {
  border: 2px dashed #800;
  margin: .5rem;
  padding: 1rem;
  font-size: 2.5rem;
}

#textPlace {
  border: 2px dashed #800;
  margin: .5rem;
  padding: 1rem;
  font-size: 2.5rem;
  width: calc(100% - 1rem);
}
dialog { text-align: center; }
dialog button { padding: 0.5rem 2rem 0.5rem 2rem };
<p id=textBlock>Привет</p>
<p><input id=textPlace type=text value="" /></p>

<dialog id="win-dialog">
  <h3>Ты победил</h3>
  <button onclick="this.closest('dialog').close()">OK</button>
</dialog>

→ Ссылка