How to execute click function before the blur function

Replace your click event with (mousedown). Mousedown event is called before blur. This code should works properly:

<input (focus)="showDropdown()" (blur)="myBlurFunc()">
<ul>
  <li *ngFor="let item of dropdown" (mousedown)="myClickFunc()">{{item.label}}</li>
</ul>

It looks like click event has lower priority than blur, so it is predictible behaviour that blur event fires first.


Here's another way to solve this, and it requires no JS:

.menu {
  display: none;
}
.toggle:focus + .menu,
.toggle + .menu:active {
  display: block;
}
<button class="toggle">menu</button>
<ul class="menu">
  <li onClick="console.log('tacos')">tacos</li>
  <li onClick="console.log('burgers')">burgers</li>
</ul>

The secret is to target not only the .toggle:focus state, but also the .menu:active state.


Make the options in your dropdown elements that can receive focus - use <a> or <button> or set tabindex="-1". Then, in your onBlur handler, check event.relatedTarget. In an onBlur handler, relatedTarget is the element receiving focus (if there is one). If it is one of the options in your dropdown, then you know not to hide the options at this point.

This is preferable over switching from onClick to onMouseDown, because an actual click doesn't always immediately follow a mouseDown. If you mousedown, move the cursor off, then mouseup, you'll trigger a mousedown without triggering a click at all.

const elToggle = document.querySelector('.toggle');
const elMenu = document.querySelector('.menu');
elToggle.onfocus = (event) => {
  elMenu.classList.add('open');
};
elToggle.onblur = (event) => {
  const didClickMenu = elMenu.contains(event.relatedTarget);
  if (!didClickMenu) {
    elMenu.classList.remove('open');
  }
};
.menu {
  display: none;
}
.menu.open {
  display: block;
}
<button class="toggle">menu</button>
<ul class="menu">
  <li tabIndex="-1" onClick="console.log('tacos')">tacos</li>
  <li tabIndex="-1" onClick="console.log('burgers')">burgers</li>
</ul>

Tags:

Angular