I'm planning a virtual keyboard. It works for simple input, but when I repeat the typing in a setInterval it only works without refocussing on the input-element, otherwise the cursor-position jumps. If anybody spots the issue (I assume wrong sequence at any point....)
const keyboard = document.querySelector(".keyboard");
const validInputEls = [HTMLInputElement, HTMLTextAreaElement];
// variables and flags
let lastFocusedInput, isTyping, typingInterval, typingTimeout;
// get key/value handle exceptions
//(currently with warnings and early returns)
function handleKeyboard(event) {
const key = event.target.closest(".key");
if (!key) return;
if (!lastFocusedInput) {
console.warn("Please first choose a target for your input!");
return;
}
if (!isTyping) {
// start typing in interval here
isTyping = true;
let char = key.dataset.value;
char = char === "Space" ? "\xa0" : char; // doesn't print " "
if (!char) {
console.warn(`Currently no action associated with ${key.innerText}-key.`);
return;
}
type(char);
typingTimeout = setTimeout(() => {
typingInterval = setInterval(() => type(char), 150);
}, 500);
}
}
function type(char) {
const cursorPosition = lastFocusedInput.selectionStart;
const text = lastFocusedInput.value;
const newValue =
text.substring(0, cursorPosition) + char + text.substring(cursorPosition);
lastFocusedInput.value = newValue;
const newCursorPosition = cursorPosition + 1;
lastFocusedInput.setSelectionRange(newCursorPosition, newCursorPosition);
}
function stopTyping() {
clearTimeout(typingTimeout); // Cancel the typing timeout
clearInterval(typingInterval);
isTyping = false;
}
// set focused if valid
document.addEventListener("focusin", (event) => {
if (!validInputEls.includes(event.target.constructor)) return;
lastFocusedInput = event.target;
});
keyboard.addEventListener("mousedown", handleKeyboard);
keyboard.addEventListener("mouseup", stopTyping);
keyboard.addEventListener("mouseleave", stopTyping);
a working example is here: https://codepen.io/BarbWire/pen/rNogpOW
I found the focus problem: Instead of resetting the focus I need to set event.preventDefault() in the handle