Passing mouse events to lower layers

1,393 views
Skip to first unread message

Maël Primet

unread,
Oct 11, 2013, 7:23:00 AM10/11/13
to d3...@googlegroups.com
Hello,

I want to make a map visualization with several layers, some of which might be SVG & some others Canvas
Ideally, I'd like that all MouseEvents be passed in order through the layers, stopping the first time the event encounters an object that processes it.

Eg there could be:
 - a lower canvas layer
 - a middle SVG layer with a circle
 - a top div layer

when the mouse clicks on the top layer above the circle, it passes the event to the middle layer, the circle.on('click') event is processed, and the bubbling stops

when the mouse clicks on the top layer outside of the circle, it passes the event to the middle layer, which passes the even to the lower layer

how would one do that?

I've tried handling events and passing them to other elements (see http://jsbin.com/aMahaMEB/25) but I can't see how to figure out to which element in the lower layer to pass the click to: I don't want to have to write my own SVG parser just to figure out which element in the SVG layer I have to pass the mouseclick to, and I'd prefer if it was possible to pass the event to the SVG element and have it bubble up to the topmost element under cursor

Maël Primet

unread,
Oct 11, 2013, 4:43:07 PM10/11/13
to d3...@googlegroups.com
I'm bumping this in case anyone knows how to do that

Victor Powell

unread,
Oct 12, 2013, 7:12:44 PM10/12/13
to d3...@googlegroups.com
Best to post example code that uses D3 in the future. 

I think you're a bit confused by how event bubbling works. Events are bubbled up the DOM, not through visible layers. see: http://bl.ocks.org/vicapow/6955981 and check the console log messages for a ruff overview of how this works.

Let me know if that doesn't help clear things up.

- Victor

Maël Primet

unread,
Oct 13, 2013, 4:23:49 AM10/13/13
to d3...@googlegroups.com
Hello, thanks for your answer,

the code does not use d3.js, but the application to d3.js is quite obvious IMHO so I figured that it will interest people here :)

as for the question, I think I understand how event bubbling works, but it was not the question, my question was: how to create a mechanism that allows several layers that are not inside each other like with event-bubbling, but rather side-by-side (covering because of an absolute CSS position) to all get the mouse events one by one until one of them captures the event.

eg.

<svg id="id1">...</svg>
<svg id="id2">...</svg>

#id2 covers #id1, and the user clicks on an element in #id2, how do I "recreate" a click event at the right position in #id1 such that the element of id1 beneath the mouse gets the click event?

Right now I manage to recreate the click event in #id2 at the root, but for some reason the event does not seem to bubble up the other layers, so only #id1.on('click') is activated, and not event handlers of objects in #id1

Ian Wilson

unread,
Oct 13, 2013, 10:48:44 AM10/13/13
to d3...@googlegroups.com
Hey Matt, just to clarify two things:
1. This isn't a d3 question, it's a general javascript question.
2. event bubbling is when an inner DOM element passes events up the chain of its parent elements until it hits the document root. You have sibling elements, so events will never propagate between them without your intervention. If you have smaller clickable elements within the piece you trigger a click on, click events will not 'bubble' to them. Events bubble up to parents, not down to children.

What you can do: Set up a global layover, just a big square that covers all your other elements, and register all your other 'clickables' dimensions with it (much like an image map). Then every time you click on the layover, you loop through that array of things registered with it, find out if the click's x and y coordinates fall within that element's dimensions, and then fire whatever function you need (most likely not a real click event, but just a function that does something).If you really want a click event, you can recreate it using jquery, something like $(myFoundElement).trigger("click",[ build an appropriate object here ]);

You could also do this registration on an element by element basis, if you figure out which elements overlap in what parts at the start. If your map generation isn't dynamic and all the elements are the same dimensions, this might be easier? I don't see a real reason to not do it all in one place though.

Ian Wilson

unread,
Oct 13, 2013, 10:50:35 AM10/13/13
to d3...@googlegroups.com
Sorry, Maël not Matt. I just woke up.

Maël Primet

unread,
Oct 13, 2013, 4:06:20 PM10/13/13
to d3...@googlegroups.com
Thanks for your answer,

as I said in the other post, I know it is not directly a d3-related question, but I guess it can help people building more involved viz, so I figured it could be here :)

As for your solution, I thought of that, but I think it would be cumbersome: I wanted to have many elements like SVG paths etc that trigger click-events, so I'd have to shadow a huge number of elements in the additional top layer and this would incur some huge overhead

Isn't there a way to reproduce the click event in a top-down fashion, just as the browser detects on which element the click has fallen? If you look at the jsbin that I sent in my original post this is the approach I've tried but all I can do is recreate an event on the lower layer (and not on the SVG element in the lower layer that is beneath the click). I thought that this would result in the event "bubbling up" in the lower layer and triggering the click on the SVG circle, but I think it does not do that
Reply all
Reply to author
Forward
0 new messages