Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to capture events before they are consumed by d3-brush #44

Closed
martinblostein opened this issue May 9, 2019 · 3 comments
Closed

Comments

@martinblostein
Copy link

martinblostein commented May 9, 2019

I understand that (since d3v4), d3-brush stops propagation of the events that it has handled, and that there are good reasons for this. However, I have been unable to use the usual rules of event propagation to get around this, as suggested in this comment on an issue raised when this new behaviour was introduced.

The behaviour I expect is that if I assign a capturing event listener on to the parent of an element upon which I have called a d3-brush object, that event should be handled before the propagation is stopped by d3-brush. This however does not work:

<!DOCTYPE html>
<meta charset="utf-8" />

<script src="https://d3js.org/d3-color.v1.js"></script>
<script src="https://d3js.org/d3-dispatch.v1.js"></script>
<script src="https://d3js.org/d3-ease.v1.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.js"></script>
<script src="https://d3js.org/d3-timer.v1.js"></script>
<script src="https://d3js.org/d3-selection.v1.js"></script>
<script src="https://d3js.org/d3-transition.v1.js"></script>
<script src="https://d3js.org/d3-drag.v1.js"></script>
<script src="https://d3js.org/d3-zoom.v1.js"></script>
<script src="https://d3js.org/d3-brush.v1.js"></script>

<div id="parent">
  <svg id="child" width="200" height="200"></svg>
</div>

<script>
  function parentListener(event) {
    console.log("Hello from parentListener");
  }
  document
    .querySelector("#parent")
    .addEventListener("click", parentListener, true);
  d3.select("#child").call(d3.brush());
</script>

The parentListener is not fired. This is contrasted with the following example:

<!DOCTYPE html>
<meta charset="utf-8" />

<div id="parent">
  <svg id="child" width="200" height="200"></svg>
</div>

<script>
  function childListener(event) {
    console.log("Hello from childListener");
    event.stopImmediatePropagation();
  }
  function parentListener(event) {
    console.log("Hello from parentListener");
  }
  document.querySelector("#child").addEventListener("click", childListener);
  document
    .querySelector("#parent")
    .addEventListener("click", parentListener, true);
</script>

In this case, both listeners fire even though the childListener stops event propagation.

If this is not the behaviour I should expect, I apologize for misunderstanding and ask if there is any recommended way to use d3-brush along with other mouse event handlers for the events of which it stops propagation.

This Stackoverflow question from 2016 asks essentially exactly what I am asking here, except for d3-zoom. Unfortunately it has not been answered.

Thank you very much for you time.

@martinblostein martinblostein changed the title Unable to catch events before they are consumed by d3-brush Unable to capture events before they are consumed by d3-brush May 9, 2019
@martinblostein
Copy link
Author

martinblostein commented May 10, 2019

I've investigated further seems that once the brush is started, d3-brush and d3-drag attach capturing event listeners to the Window object that stop propagation of mouse events. This seems very hard to work around.

For example, see here:

      var view = select(event.view)
          .on("keydown.brush", keydowned, true)
          .on("keyup.brush", keyupped, true)
          .on("mousemove.brush", moved, true)
          .on("mouseup.brush", ended, true);

As far as I can tell from debugging, event.view always seems to return a selection of the window object. Is this expected behaviour?

@mbostock
Copy link
Member

mbostock commented Aug 2, 2019

I’m not able to reproduce this problem; for me the click listener on the parent element is invoked as expected. I tried both your test case and my own:

https://observablehq.com/d/066a8c3e6e0c06f0

However, I should note that the click will be prevented if the mouse moves at all between mousedown and mouseup. In that case, the gesture is interpreted as a brush gesture, and thus it is considered handled by the brush and consumed, preventing a subsequent click. If you have a very sensitive mouse, it’s possible that the mouse is moving a tiny amount on mousedown or mouseup, which is causing the click to be prevented.

d3-drag and d3-zoom support configurable click distance thresholds (drag.clickDistance and zoom.clickDistance), but d3-brush does not yet. If you would like this feature, please vote on #51.

Thank you!

@mbostock mbostock closed this as completed Aug 2, 2019
@martinblostein
Copy link
Author

martinblostein commented Nov 16, 2019

Hi there, thanks for responding to the issue, it fell off my radar. I checked my first again example and it seems that the parent clicks are registered, except when the user clicks off of the brush -- that click appears to be lost. This is definitely a smaller issue than what I thought I was originally experiencing, but I thought it was worth bringing to your attention.

Note: when I revisited my first exampe, I replaced all of the separate script tags with <script src="https://d3js.org/d3.v5.min.js"></script>, as there seemed to be an issue with sourcing d3-dispatch.v1.js.

I don't think my issue is related to the click distance threshold, I've reproduced this using a touch pad with a separate mouse button. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants