New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 768555 link

Starred by 1 user

Issue metadata

Status: WontFix
Owner: ----
Closed: Sep 2017
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Linux
Pri: 2
Type: Bug



Sign in to add a comment

extra 'cilck' event fired on parent even when no mousedown/mouseup has fired.

Project Member Reported by dpa...@chromium.org, Sep 25 2017

Issue description

Minimal repro example at https://jsfiddle.net/qyxnkduy/8/ (reproduced on Linux, have not tried other platforms yet).

Repro steps 
 - Have an element (inner) inside another element (parent). The inner element has a 'click' handler which calls stopPropagation().
 - Click and hold within the inner element.
 - Drag pointer to the parent (outside the inner element).
 - Release pointer

Expected: No click event should be fired.
Actual: A 'click' event is fired with the parent as the target/currentTarget element.

Problem does not reproduce on Firefox.


 
Cc: mustaq@chromium.org dtapu...@chromium.org
Labels: Hotlist-Input-Dev
Owner: nzolghadr@chromium.org
Status: Assigned (was: Untriaged)
Can reproduce this on Linux.

Comment 2 by mustaq@chromium.org, Sep 26 2017

Not sure I am following the repro: the inner element ("#end") doesn't receive a "click" event in this case because it never received a "mousedown".  So the stopPropagation() call is never executed.  This looks like WAI unless I am missing something.

Comment 3 by dpa...@chromium.org, Sep 26 2017

@mustaq: Are you saying that Firefox's and Safari's (just tried Safari) behavior is wrong and Chrome's is the right one?  

I understand that the inner 'click' handler never executes, and therefore stopPropagation() is never called. But please see this from the user's perspective, and ignore the internal implementation details for a sec.

To reverse the question/problem: How can the user prevent the 'click' event from being fired on the outer div? I tried various combinations of preventDefault()/stopPropagation() without success. The only solution I could find was to manually maintain a boolean (sse https://jsfiddle.net/fw8pu6x8/2/).

What exaggerates the problem is that the extraneous 'click' event has
event.target === parent // true,
event.currentTarget === parent // true

leaving no way to the user to determine whether the click was initiated within the child element. It basically looks 100% the same as if the user did mousedown + mouseup on the parent itself, which is not true.

See also related question in SO (https://stackoverflow.com/questions/45042456/how-to-prevent-parent-click-event-when-dragging-from-child-to-parent).

Comment 4 by dpa...@chromium.org, Sep 26 2017

Also

> Not sure I am following the repro: the inner element ("#end") doesn't receive a "click" event in this case because it never received a "mousedown"

Did you mean "mouseup" here? The inner element receives a "mousedown" but not a "mouseup".

Comment 5 by mustaq@chromium.org, Sep 26 2017

The spec requires that "click" is fired to the nearest common ancestor of "mousedown" and "mouseup" targets:
https://w3c.github.io/uievents/#events-mouseevent-event-order

In your repro, "mousedown" goes to "#start", "mouseup" goes to "#end", so "click" is correctly dispatched to "#outer".  And we have .target == .currentTarget because the "click" was dispatched to "#outer" (and NOT bubbled to "#outer").

I can't explain the FF behavior here, it doesn't look like spec compliant (the "stopPropagation" is useless in the repro anyways).

Comment 6 by dpa...@chromium.org, Sep 26 2017

Status: WontFix (was: Assigned)
Summary: extra 'cilck' event fired on parent when mousedown occurred on child and mouseup on parent (was: Event stopPropagation() ignored and erroneous 'cilck' event is fired)
I rephrased the title of this bug to not mention stopPropagation() to avoid confusion.

Regarding the spec, yes I see your point now, specifically this part 

"SHOULD fire click and dblclick events on the nearest common inclusive ancestor when the associated mousedown and mouseup event targets are different",

and also further explained per example 6.

Having said that IIUC, the spec basically provides no way of telling that the mousedown occurred in a separate element than the mouseup, just by looking at the 'click' event. Anyway, thanks for the explanation.

Comment 7 by dpa...@chromium.org, Sep 27 2017

Cc: nzolghadr@chromium.org
Owner: ----
Status: Untriaged (was: WontFix)
Summary: extra 'cilck' event fired on parent even when no mousedown/mouseup has fired. (was: extra 'cilck' event fired on parent when mousedown occurred on child and mouseup on parent)
@mustaq: I studied the specs further, and I think I found why Chrome's behavior is not spec compliant, with this updated repro case, https://jsfiddle.net/jgd9bt2x/6/

Why is it not spec compliant?

1) The PointerEvents API specifies a way to prevent firing mousedown/mouseup events, see section https://w3c.github.io/pointerevents/#mapping-for-devices-that-support-hover, bullet point 5. This actually works in Chrome (doesn't in Safari).
2) The spec you referred to previously, at https://w3c.github.io/uievents/#events-mouseevent-event-order, specifies that a 'click' event should be fired if both mousedown and mouseup fire (even if they fire on different elements). But with the updated example, those do not fire on any element. Yet, the click event is still dispatched, erroneously I think.

Can you please take another look (setting the status back to Untriaged).

Comment 8 by mustaq@chromium.org, Sep 27 2017

Status: WontFix (was: Untriaged)
1) Safari doesn't support PointerEvents :(

2) The UIEvent spec predated the PointerEvent spec, so it doesn't mention "pointerup/down" events.  Preventing pointer events doesn't affect "click", see the Note in https://w3c.github.io/pointerevents/#compatibility-mapping-with-mouse-events.

Comment 9 by dpa...@chromium.org, Sep 27 2017

Thanks for the link, that explains it.

Having said that, as a user of the Web the end result of how those Specs (don't) interact with each other is a bit frustrating.

Specifically the fact that calling element.setPointerCapture() should treat all subsequent (Pointer) events as if they happened on that element regardless of pointer coordinates, but that does not apply to the 'click' event (the one fired on the parent) is a bug in the spec in my opinion. I suppose that the right place to have that discussion is at https://github.com/w3c/pointerevents/issues, so I'll follow up there.

Thanks again.
This is related to issue 689158. We tried fixing it and comply with the spec but that caused some regressions that we had to revert it. We are still not sure how to proceed with that bug.
To clarify, nzolghadr@ referred to the issue with click on captured pointer.
@nzolghadr: Thanks for the context, I starred that bug for future reference.

@mustaq: My original use case (which led me to file this bug) involves setPointerCapture anyway.

FWIW, I am posting some more details of my use case (see screencast), showing why current behavior is making something that should be simple, more complicated (thankfully I've found a workaround).

Specs of the UI component in question (a toggle button with a label):
1) Clicking on the outer element (label) should toggle the control.
2) Dragging the toggle button itself should toggle the control, only if dragged towards the correct direction.

What happens is that after I drag the element with scenario 2 and release while the pointer is within the bounds of the outer element, 1 is also accidentally triggered, toggling the button a 2nd time.
capture_use_case.mp4
1.1 MB View Download
dpapad@ that's actually an interesting use case. So assume we have a browser that is  spec compliant and it sends the click event to the first common ancestor of the elements that received pointerdown and up (which are impacted by capture APIs). Now if you use pointer capture for your usecase (i.e. handling the mouse moves outside of that toggle button) and also toggling the button with click handler then you would run into problem anyway. Is that correct? What is your workaround?
The related spec issue that I filed back then is this one in case you would like to follow the discussion there and chime in:
https://github.com/w3c/pointerevents/issues/75

@nzolghadr: You can see my latest approach at [1], where cr_toggle is the inner element, and settings_toggle_button is the outer. There are some Polymer specific bits in that CL, like a synthetic 'tap' event instead of the native 'click', but the main point remains.

The 'click' on the inner element is easily ignored via a boolean that tracks whether a pointermove fired after pointerdown and before pointerup. If no pointermove fired, the user just wants to toggle the inner element. If a pointermove fired, then the user wants to drag the inner element, so the 'click' should be ignored.

The 'click' on the outer element (which is the event this bug was filed for), is now ignored by keeping track of the |timeStamp| of the last 'pointerup' event. I've noticed that the extraneous 'click' event has the exact same |timeStamp| with the last 'pointerup' on the inner element, see the shouldIgnoreHostTap(Event) method I am inctroducing and calling from the parent.

[1] https://chromium-review.googlesource.com/c/chromium/src/+/686041
@nzolghadr: FYI, posted at the related spec discussion about this, see https://github.com/w3c/pointerevents/issues/75#issuecomment-349494814.
Thanks. I replied there as well. We can continue the discussion there.

Sign in to add a comment