0

I'm listening to the onFocus and to onClick events on the same input-element. The onFocus should always set a boolean to true and the onClick event handler should toggle this boolean.

But on a click both events are fired with the onFocus at first, so the boolean is being set to true first and then toggled by the onClick. How can I change this behaviour?

I tried to use stopPropagation inside the onClick event handler. Also setting a isClicked flag did not help. (Probably because both events are fired at the same time) (see JSFiddle)

let input = document.getElementById("input");
let isOpen = false

input.onclick = () => {
    isOpen = !isOpen
}

input.onfocus = () => {
    isOpen = true
}

input.onblur = () => {
    isOpen = false
}

JSFiddle: http://jsfiddle.net/coh29gwe/7/

At the first click on the input the isOpen flag should be true but is false.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
WayneJohn
  • 53
  • 6
  • Prevent default can be used, if nothing else, then for `isDefaultPrevented` – Akxe May 27 '19 at 16:01
  • The design is perhaps flawed. Since you're also setting the Boolean to false when losing the focus, what's the point of toggling when clicked? Just tab off the element to toggle. Or have a separate control that toggles the Boolean when the input has focus. – Heretic Monkey May 27 '19 at 16:06
  • @Akxe I tried this and there are 2 different events fired. `defaultPrevented` is still set to false in the onClick handler. – WayneJohn May 27 '19 at 16:45

2 Answers2

0

there are some confuse when you are setting isOpen variable. I think you should use hasFocus property of this element to indicate the opening state Get focus state of an element

or you can validate the value of isOpen in onClick() before you set it

if (isOpen == false){
    isOpen = true;
}

Because both of onFocus and onClick are fired when you perform a click thus you only need to set isOpen to true when onClick is called without onFocus (via script other some way that we don't know)

Nam Vi Nguyen
  • 83
  • 2
  • 8
  • Thank you, there is indeed a mistake from me with the isClicked. Using hasFocus makes more sense here. I have an updated code: http://jsfiddle.net/zeu8nv5g/2/ Only Issue is this case: First focus, you need 2 Clicks to toggle. But I think I can live with this solution. – WayneJohn May 27 '19 at 16:51
  • I saw your code, and this is a coding mistake that you have set hasFocus to false. The onFocus() is called only 1 when you click 2 times because it has already focus in the second time thus has Focus isn't called again. You can take a look in here to see the change of element state http://jsfiddle.net/yojs6b4z/1/ – Nam Vi Nguyen May 27 '19 at 17:09
  • ah you ment the `document.hasFocus()`, this always will be true, because the onFocus is set first. – WayneJohn May 27 '19 at 17:11
  • and the **toggle** of the `isOpen` is the important part in my idea :-) which is missing in your code – WayneJohn May 27 '19 at 17:16
  • yeah, i am understanding your idea know. Sorry for my not related answer before. http://jsfiddle.net/9hx21b6a/2/ i have updated my code, when onClick is called, it will reset the state of hasFocus and process base on the state. I know it not clean and may not work well in some case, please tell me the better answer if you found it. – Nam Vi Nguyen May 27 '19 at 17:40
0

This will solve your problem. It will allow only one update at one time.

let input = document.getElementById("input");
let isOpen = false

let updated = false;
function updateOpenedStatus(newStatus) {
    if (!updated) {
        isOpen = newStatus;
        updated = true;
        setTimeout(() => updated = false, 0);
    }
}

input.onclick = () => {
    updateOpenedStatus(!isOpen);
}

input.onfocus = () => {
    updateOpenedStatus(true);
}

input.onblur = () => {
    updateOpenedStatus(false);
}

However, as it seems you might want to change the behaviour altogether, as this aria navigation-menu and this handles events differently from your implementation.

Akxe
  • 9,694
  • 3
  • 36
  • 71
  • This basicaly works. Allthough I need to set the timeout to 150 for Firefox or Edge. And it might depend on the operating system. But the approach is good. – WayneJohn May 29 '19 at 10:37
  • @WayneJohn Well, that is too bad, this number could change at any point... – Akxe May 29 '19 at 13:08