0

I have a MDC Menu component, where I would like to setup a click or select event listener for list items. I have tried with various events like 'click', 'select', 'selectionchange', 'selectstart', but none of them worked. In documentation it says that MDCMenu:selected

Used to indicate when an element has been selected. This event also includes the item selected and the list index of that item.

I have tried to listen to that event as well, but nothing has worked:

menu.listen("MDCMenu:selected", e => console.log(e));

You can see the sandbox here. How should the event listener be setup for MDC menu component?

Update

Since after I was informed in the comments that for other users the code is actually working. I went to test it myself with other browsers, and in safari and firefox it was working fine on my mac machine, but in Chrome I still have a problem. I have the chrome version 83.0.4103.61. I have updated the codesandbox with suggestions from the comments, but I can now see that if press few times select options, that it starts to work very randomly all of a sudden in all of the browsers.

Leff
  • 1,968
  • 24
  • 97
  • 201
  • What's the problem? As soon as I click the input field, the menu drops down, and when I click a menu item, I see the results of `console.log()` caused by your listener. – kmoser May 22 '20 at 22:42
  • @kmoser I am not sure why, but I very rarely and randomly get something in the console when I run it in the codesandbox that I posted a link to – Leff May 22 '20 at 22:44
  • Not sure what to tell you since it's consistent for me in both Firefox and Chrome (Windows). What browser are you using? – kmoser May 22 '20 at 22:48
  • I have tested it now, and it works for me on both safari and firefox, but not on chrome – Leff May 22 '20 at 22:50
  • What version of Chrome? – kmoser May 22 '20 at 22:53
  • I just spend nearly 1-2 hours figuring out why it wasn't working (it wasn't working for me as well) and found a work around, but after reading the comments I forked the sandbox and it suddenly worked! – Swetank Poddar May 22 '20 at 23:01
  • @SwetankPoddar even after a fork it still works very randomly for me in the chrome – Leff May 22 '20 at 23:08
  • I had a strong feeling that the event `MDCMenu:selected` is a part of the `MDCMenuFoundation`. So what I did was I got the default foundation and then `init` it as per the documentation. Have a look, let me know if it works... https://codesandbox.io/s/snowy-resonance-5utxg?file=/src/index.js – Swetank Poddar May 22 '20 at 23:13
  • @SwetankPoddar I have tried your suggestion, but it is still working very randomly, if you press enough time select options, you will see that the result is not seen every time, I have made a new fork of the codesandbox : https://codesandbox.io/s/clever-elion-s2xrz?file=/src/index.js – Leff May 22 '20 at 23:30

1 Answers1

1

It appears that the inconsistency is due to a race condition. Clicking on the menu causes the focus to leave the input which causes the menu to close. And the menu closing causes focus to move back to the input making it open again.

The issue is that the menu often closes before the menu has a chance to send out the selected event.

You need to either prevent the menu from closing on focusout or set a generous timeout before closing the menu.

input.listen("focusout", () => {
  setTimeout(() => {
    menu.open = false;
    // The timer might need to be fiddled with. Needs to not be too big or too small.
  }, 120);
});

https://codesandbox.io/s/new-brook-o7k1j?file=/src/index.js


Here's another option without the timing issues of setTimeout. It uses timeouts of 0 to mimic setInterval in order to re-order the timing of events instead by pushing them to the end of the event queue. This should be safer and less prone to the race condition issue from before.

let menuFocused = false;
input.listen("focusin", () => {
  if (!menuFocused) menu.open = true;
});
input.listen("click", () => {
  menu.open = true;
});

menu.listen("focusin", () => {
  menuFocused = true;
});
menu.listen("focusout", () => {
  // This interval is to help make sure that input.focusIn doesn't re-open the menu
  setTimeout(() => {
    menuFocused = false;
  }, 0);
});
input.listen("focusout", () => {
  setTimeout(() => {
    if (!menuFocused) menu.open = false;
  }, 0);
});

https://codesandbox.io/s/proud-hooks-c9qi5?file=/src/index.js

Zachary Haber
  • 10,376
  • 1
  • 17
  • 31