This has got to be really simple, but I just didn't find any answer. I have a simple menu to enter special characters into a search text box. Its doing its job but if i click a character the menu will (blur) and the focus is not returned to the field. As long as the cursor is in the field and/or I click on the characters menu, I want the menu to remain visible. On the event listner for the menu buttons, the first and last lines work. The text is appended to the search box, and hello world logged to the console. But the menu does not show, and the text box does not regain the focus. What is wrong here?
let searchInput = document.querySelector('#search-input');
let charsMenu = document.querySelector('.charsmenu');
// add event listeners to textbox
searchInput.addEventListener('focus', function() {
toggleDisplay(charsMenu, true);
});
searchInput.addEventListener('blur', function() {
toggleDisplay(charsMenu, false);
});
function toggleDisplay(element, show) {
if (show)
element.style.display = 'block';
else
element.style.display = 'none';
}
let listItems = document.querySelectorAll('.charsmenu li');
listItems.forEach((item, index) => {
item.addEventListener('mousedown', (event) => {
searchInput.value += `${event.currentTarget.innerHTML}`;
toggleDisplay(charsMenu, true);
searchInput.focus();
console.log("Hello world!");
});
});
Chances are, after mousedown is fired, the <li> element is going to steal focus away from the input element. Therefore you will want to defer setting focus on the input element at the end of the call stack, i.e.:
window.setTimeout(() => searchInput.focus(), 0);
See proof-of-concept below:
let searchInput = document.querySelector('#search-input');
let charsMenu = document.querySelector('.charsmenu');
// add event listeners to textbox
searchInput.addEventListener('focus', function() {
toggleDisplay(charsMenu, true);
});
searchInput.addEventListener('blur', function() {
toggleDisplay(charsMenu, false);
});
function toggleDisplay(element, show) {
if (show)
element.style.display = 'block';
else
element.style.display = 'none';
}
let listItems = document.querySelectorAll('.charsmenu li');
listItems.forEach((item, index) => {
item.addEventListener('mousedown', (event) => {
searchInput.value += `${event.currentTarget.innerHTML}`;
toggleDisplay(charsMenu, true);
window.setTimeout(() => searchInput.focus(), 0);
});
});
.wrapper {
display: flex;
}
.charsmenu {
display: none;
list-style: none;
}
.charsmenu li {
float: left;
border: 1px solid black;
padding: 16px;
}
<div class="wrapper">
<ul class="charsmenu">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<input type="text" id="search-input" />
</div>
As Terry said, the focus is being stolen anyway.
My solution is just add preventDefault() before the focus setting:
listItems.forEach((item, index) => {
item.addEventListener('mousedown', (event) => {
searchInput.value += `${event.currentTarget.innerHTML}`;
toggleDisplay(charsMenu, true);
event.preventDefault();
searchInput.focus();
});
});
By the way, you can have event listener on a <UL> instead of each <LI>.