I have an input field that brings up a custom drop-down menu. I would like the following functionality:
- When the user clicks anywhere outside the input field, the menu should be removed.
- If, more specifically, the user clicks on a div inside the menu, the menu should be removed, and special processing should occur based on which div was clicked.
Here is my implementation:
The input field has an onblur()
event which deletes the menu (by setting its parent's innerHTML
to an empty string) whenever the user clicks outside the input field. The divs inside the menu also have onclick()
events which execute the special processing.
The problem is that the onclick()
events never fire when the menu is clicked, because the input field's onblur()
fires first and deletes the menu, including the onclick()
s!
I solved the problem by splitting the menu divs' onclick()
into onmousedown()
and onmouseup()
events and setting a global flag on mouse down which is cleared on mouse up, similar to what was suggested in this answer. Because onmousedown()
fires before onblur()
, the flag will be set in onblur()
if one of the menu divs was clicked, but not if somewhere else on the screen was. If the menu was clicked, I immediately return from onblur()
without deleting the menu, then wait for the onclick()
to fire, at which point I can safely delete the menu.
Is there a more elegant solution?
The code looks something like this:
<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />
var mouseflag;
function setFlag() {
mouseflag = true;
}
function removeMenu() {
if (!mouseflag) {
document.getElementById('menu').innerHTML = '';
}
}
function doProcessing(id, name) {
mouseflag = false;
...
}
I was having the exact same issue as you, my UI is designed exactly as you describe. I solved the problem by simply replacing the onClick for the menu items with an onMouseDown. I did nothing else; no onMouseUp, no flags. This resolved the problem by letting the browser automatically re-order based on the priority of these event handlers, without any additional work from me.
Is there any reason why this wouldn't have also worked for you?
Replace on onmousedown
with onfocus
. So this event will be triggered when the focus is inside the textbox.
Replace on onmouseup
with onblur
. The moment you take out your focus out of textbox, onblur will execute.
I guess this is what you might need.
UPDATE:
when you execute your function onfocus-->remove the classes that you will apply in onblur and add the classes that you want to be executed onfocus
and
when you execute your function onblur-->remove the classes that you will apply in onfocus
and add the classes that you want to be executed onblur
I don't see any need of flag variables.
UPDATE 2:
You can use the events onmouseout and onmouseover
onmouseover-Detects when the cursor is over it.
onmouseout-Detects when the cursor leaves.
A more elegant (but likely less performant) solution:
Instead of using the input's onblur to remove the menu, use document.onclick
, which fires after onblur
.
However, this also means that the menu is removed when the input itself is clicked on, which is undesired behaviour. Set an input.onclick
with event.stopPropagation()
to avoid propagating clicks to the document click event.
change onclick by onfocus
even if the onblur and onclick do not get along very well, but obviously onfocus and yes onblur. since even after the menu is closed the onfocus is still valid for the element clicked inside.
I did and it worked.
You can use a setInterval
function inside your onBlur
handler, like this:
<input id="input" onblur="removeMenu()" ... />
function removeMenu() {
setInterval(function(){
if (!mouseflag) {
document.getElementById('menu').innerHTML = '';
}
}, 0);
}
the setInterval
function will remove your onBlur
function out from the call stack, add because you set time to 0, this function will be called immediately after other event handler finished