angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.35k stars 6.74k forks source link

Menu: Overlay is blocking click and contextmenu events #9320

Open henev opened 6 years ago

henev commented 6 years ago

Bug, feature request, or proposal:

Bug

What is the expected behavior?

When menu is open, any click event (including context menu events) outside of the menu and targeting clickable items, is expected to work from the first time. Menu should be closed along with the action handled by the clicked item.

What is the current behavior?

Overlay is blocking click events (context menu events). Clicking on the overlay first closes the menu and the user needs to click again to do his desired action (e.g. clicking navigation item that).

What are the steps to reproduce?

  1. Go to documents/demo page.
  2. Navigate to Components -> Menu
  3. Click on the first example for demo
  4. Try to click any other component page from the left navigation - Component page will open after second click attempt ( after the menu is closed ).

What is the use-case or motivation for changing an existing behavior?

The current functionality is a bad UX decision. Users usually expect to execute actions with one click (closing menu is counted towards the executed action). Making the user to click twice to close the menu and then proceed to the desired action is frustrating and not likely acceptable by most projects.

It also causes issues if developers want to do custom context menu with the Menu component.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

VoloDziu commented 6 years ago

Agree. In my case I would like to get behaviour similar to that of Google Inbox:

screenshot 2018-01-15 14 42 09

In this example, I can open "more actions" menu but still interact with "quick-access" items with one click.

A minor related issue is that overlay also prevents hover states on current items, which reveals the list of actions (again, see Google Inbox for example).

utkant commented 6 years ago

Any progress on this. I am having this problem on the auto-complete component as well. Have to click twice on item to get it selected. From what I can see it is due to the handler set on the document by the overlay. Its like it cancels events when the input is being blurred or something. I would suspect there to be a cancelbubble/propagation problem somewhere...

cgatian commented 6 years ago

Has anyone come across a workaround for this?

rshelnutt commented 6 years ago

Baffling how such a glaring issue as this hasn't been addressed yet. Hours researching this and the closest solution I've come across is using click simulation. Anyone actually have something more concrete?

[UPDATE] Very hack-ish, but wrote and threw this in at bottom of page. Seems to be working but I'm sure could use some cleanup.

[UPDATE 2] Reworked script to detect for document click in your navigation area - (Make sure to add a CUSTOM CLASS on your parent, change the class in referenced area below, and verify the DOM traversal target, relative to your active button.)

Stripped re-trigger of already open menu - now force closes active menu. Script adds "mat-menu-active" class to event target; fails menu open if target's "mat-menu-active" class is true, pass if false.

Note: Make sure if you are using any icons or other objects inside of your menu item (like a button) that they're set with pointer-events:none in your CSS so pointer targeting is more clean.

For the love of Athena, if anyone knows a better way to clean the below mess up, please feel free. I can't imagine this would be fully suitable for production code in an Angular environment, but it gets the job done. Still learning on my end.

<script type="text/javascript">

  function matMenuClearActive () {
    var els = document.querySelectorAll('.mat-menu-active');
    for (var i = 0; i < els.length; i++) {
      els[i].classList.remove('mat-menu-active')
    }
  }

  document.addEventListener("click",function(e){
    var cdkBack = document.getElementsByClassName("cdk-overlay-backdrop")[0];
    var posX = event.clientX, posY = event.clientY;

    //if overlay is down & doesn't contain active class
    //add a custom class to your menu container and check dom target on the following IF statement to match what you have
    if ((e.target.parentElement.parentElement.classList.contains("CUSTOM CLASS")) && (!e.target.classList.contains("mat-menu-active")) ) {
      matMenuClearActive();
      e.target.classList.add("mat-menu-active");
    } else {
      // if overlay is up
      if (cdkBack){
        cdkBack.addEventListener("click", function () {
          if (!document.elementFromPoint(posX, posY).classList.contains("mat-menu-active")) {
            matMenuClearActive();
            document.elementFromPoint(posX, posY).click(); //recycles function
          } else {
            matMenuClearActive();
          }
        }, true);
      }
    }
  },true);

</script>
texmax11 commented 6 years ago

If a backdrop isn't required in your case, I suggest you use the hasBackdrop directive on the MatMenu and set it to false

gwenaellarmet commented 5 years ago

Hello everyone, any news on this issue ? Or is the only solutions the hacky simulated click ?

JonWallsten commented 4 years ago

@jelbourn: I've been following this issue and the PR that was just merged for some time. We're using backdrop right now to prevent multiple menus being open at the same time. Today I disabled the backdrop and added the new outsidePointerEvents observer to the mat menu trigger to hide the current menu whenever an outside click is detected. It works fine for my use case, but not sure about side effects. I know this issue is for a menu WITH backdrop, but I guess it would be really hard to do this with the backdrop unless you remove pointer events from the backdrop, but then what's the point of having it?

I will open a PR with my fix when it's ready and we'll see if my approach is good enough.

Ribosom commented 2 years ago

For me the problem is that cdk-overlay-connected-position-bounding-box is preventing any clicks because it has too big dimensions.

I added following styles:

.cdk-overlay-connected-position-bounding-box
  width: min-content !important
  height: min-content !important

This is a hack and it would be better the underlying problem would be fixed (Tested with "@angular/material": "14.2.5")

The hack can break other components, for example I had to adjust datepicker also:

.mat-datepicker-content
  width: min-content !important
  height: min-content !important
albanx commented 1 year ago

This issue is very annoying and bad UX. I have been monitoring user sessions, and every use after clicking on a Angular material dropdown, has to click twice the Search button (or any other clickable element) to take effect, looks like a glitch, like an issue, like a bad click.

I notice that the cdk-overlay-container covers all the screen causing this bug.