This problem happens to involve D3-specific functions, but is not a D3-specific problem. I have two D3 charts that I have created and attached zoom functionality to, with the goal of keeping the zoom scale and transform positions equal, such that when one chart is zoomed, the other will automatically zoom to the same scale and location. To do this, I trigger a custom event when one chart is zoomed so that I can automatically adjust the other chart:
// Within the chart definition:
var svg = ...; // The chart body definition
var zoom = d3.behavior.zoom()
.on("zoom", function(e){
// do stuff...
$("#chart-div").trigger({
type: "my.zoom",
translate: d3.event.translate,
scale: d3.event.scale
});
});
svg.call(zoom);
I then create methods that allow me to adjust the zoom level of each chart manually, triggering the D3 zoom event in the process:
var adjustZoom = function(t,s){
svg.transition().duration(500).call(zoom.scale(s).translate(t).event);
};
I then attach event listeners to each of my charts, so that when I interact with one of them and cause its zoom level to change, the other is updated automatically:
$("#chart-div1").on("my.zoom", function(e){
chart2.adjustZoom(e.translate, e.scale);
});
$("#chart-div2").on("my.zoom", function(e){
chart1.adjustZoom(e.translate, e.scale);
});
The obvious problem is that calling the adjustZoom method triggers the D3 zoom event, so attaching event listeners on both charts creates cyclical events, as each chart tries to adjust the other one infinitely.
1. chart1 [manual zoom] --> trigger d3.zoom --> triggers my.zoom
2. chart2 [my.zoom listener] --> trigger d3.zoom --> triggers my.zoom
3. chart1 [my.zoom listener] --> trigger d3.zoom --> triggers my.zoom
etc...
What I would like to implement is a way to check whether the my.zoom event has already been fired when deciding to trigger another my.zoom event, to prevent the cyclic firing of events (prevent step 3 in the above example), but I don't know how to go about doing this. Is there anyway for a function or event to be aware of the event that triggered it and then halt itself?
The first option that comes to mind, if translate and scale aren't fuzzy values, is to make calling adjustZoom with the current translate and scale a no-op. So if chart1's values change, it raises the event, changes chart2's values, and chart2 raises the event, but chart1 breaks the cycle because its translate and scale are already those values.
Another option is to pass the source element of the change in the event, and then not trigger it on the chart if that's the chart that started it.
E.g., when triggering:
$("#chart-div").trigger({
type: "my.zoom",
translate: d3.event.translate,
scale: d3.event.scale,
source: this // <=== The new bit
});
And then when handling:
if (chart2[0] !== e.source) {
chart2.adjustZoom(e.translate, e.scale);
}
Note that that code example assumes chart1 and chart2 are jQuery wrappers around single elements; if not, adjust accordingly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With