Riddle me this: how would you build a menu component that closes automatically and why?


#1

I have a menu component that opens when you click. I need it to close whenever the user clicks on option or anywhere else on the page. How would you solve this problem and what are the reasons for your approach? I can think of a couple but few seem best. The sub points are critiques of each approach.

  1. portal approach (RFC 287, ember-wormhole, ember-elsewhere)

    • The modal example is great, but I’m looking for something that does not require the user to explicitly acknowledge the menu
  2. click masking based on the portal approach

    • behaves as a context menu and requires CSS for behavior. Also, why do I need to add this filler div when all I want is to catch a globally bubbled event?
  3. global event registration / teardown by the menu component

    • nicely contained within the component but violates component contracts by introducing global click behavior
  4. click handler service to manage document.addEventListener approach in #2.

    • while fitting into Ember architecture, what significant value does this bring? From the perspective of event listening, window.document and Ember.Service are both long lived.
  5. a component in your application.hbs that wraps everything plus a service

    • cache invalidation issues and spreads the behavior across the app

Feel free to open a PR with a solve-your-idea branch in that repo!


#2

I’ll defer Ember solutions to more experienced Ember devs here but a general question:

What solution brings the least amount of overhead to your project? A traditional event JS DOM listener, while an older solution, will probably create the least amount of headache in negotiating the rest of your files.
Especially if only needing to be fired once.

Then again, if you need other requirements for that component elsewhere in the project not layed out here - perhaps that would be redundant within the scope of the Ember app?

Hope this helps at all.

Carlo(BigRubyPy)


#3

I recently made a quicksearch type component which does something similar (but is based around an input and a list of options instead of a button and a list of options and just did this in my component:

  focusIn() {
    this.set('showResults', true);
  },

  focusOut(evt) {
    // if new focus target is not contained within this component, close the menu
    if(!this.element.contains(evt.relatedTarget)) {
      this.set('showResults', false);
    }
  },

No idea if this is a good approach or what you’re looking for, I just kinda threw it in because it worked for what I needed.

Also this only handles the case where it’s not clicking on an item though, for that I handle those explicitly with actions.

I’m also not sure how this appears from an accessibility perspective (probably not great) but I’d think that relying on focus instead of just click is better. :man_shrugging: