I have a visualization that is essentially a series of stacked bar charts, each of which contains several panels. For example, here are three such bar charts, each with four panels.
I have managed to implement pan/zoom functionality that is coordinated across the charts. This is what it looks like if I zoom into the third panel from the last image, for example. The zoom behavior is called from an invisible rectangle that is placed over each chart.
My problem is that I want to enable tooltip functionality based on the location of the user's cursor within a plot. Since the zoom-rectangles are placed on top of the charts, however, no mouse events are registered for any SVG elements in the actual charts themselves.
Des anyone know a way around this?
I was following Mike Bostock's example, and like you placing a rect
across my whole chart and then calling the zoom behaviour on that, and like you found that it was consuming all the pointer events.
I found an example here that seemed to be achieving what I wanted, and I found that if I scrap the rect
and just call the zoom behaviour on the svg
element directly, I still get pointer events for the child elements.
I'm a noob here, I don't really understand why this works. I also guess this might have its own ramifications e.g. I guess this stops you limiting the area of your graphic in which mouse events cause a zoom. You may notice that the example I linked creates a sub-svg
; I don't know, but perhaps this is to solve that problem.
You can probably put the mouseevent on the same rectangle you are using for detecting your zoom. Hard to say for sure without a code sample, but I would expect you can do something along these lines:
svg.select("rect.overlay")
.on("mouseover.tooltip", function() { tooltip.style("display", null); })
.on("mouseout.tooltip", function() { tooltip.style("display", "none"); })
.on("mousemove.tooltip", mousemoveFunc);
Adding the ".tooltip" to the event adds a namespace to the event, so if you end up having any collision with your zoom listeners, you can add a namespace to them too.
In your css put the style ponter-events:none for your zooming rectangles. That way the cursor events will be sensed by the elements blow.
I know this is way too late, but I've just figured out a workaround. For me the crucial thing is order of adding bits to the svg.
svg1.append("rect")//put the rectangle for zoom events on the bottom
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.call(d3.zoom()
.scaleExtent([0.5, 10])
.on("zoom", zoomed));
var g = svg1.append("g"); //add g element for visualisation (above the rectangle).
function zoomed() { //zoom around the g's (g has to be before this, but after the rectangle)
g.attr("transform", d3.event.transform)
}
Then a little bit later on add my force elements to the g
var nodes = g.append("g")
.attr("class", "nodes")
.selectAll("circles")
.attr('id', function(d) {
return d.n_id
})
etc.
Slight issue here is I can't actually zoom with my mouse over the circles, but I have loads of white space. I'm going to try and allow propagation of zoom events, or call zoom events from the circles