1
votes

dc.js https://dc-js.github.io/dc.js/ is an extension of d3.js that has some extra UI features like the brush feature where user's can select a range by click dragging on the chart - https://dc-js.github.io/dc.js/examples/scatter-series.html

I was looking through the examples on: https://dc-js.github.io/dc.js/examples/ and the brush in docs @ http://dc-js.github.io/dc.js/docs/html/dc.coordinateGridMixin.html#brushOn__anchor and was wondering if there was a way to mirror the brush across multiple charts that have the same domain?

I was hoping to have several series charts stacked on top of each other and when the user selects a range with the brush, it would select a range on all 3 charts (not zoom on them like the multiple focus charts example https://dc-js.github.io/dc.js/examples/multi-focus.html) just show the selection brush on each chart.

( ) = brush selection range | | = chart

Before brush selection:

Chart 1 - |            |
Chart 2 - |            |
Chart 3 - |            |

After brush selection

Chart 1 - |    (     ) |
Chart 2 - |    (     ) |
Chart 3 - |    (     ) |
1

1 Answers

1
votes

Note: this solution cause problems when connected to other charts or real data.

The proper solution has stalled due to need of a general implementation for all charts, see:

https://github.com/dc-js/dc.js/issues/681

https://github.com/dc-js/dc.js/issues/682

The problem with what I am about to present is that it relies on

dc.constants.EVENT_DELAY = 0;

The debounce in dc.js is mostly a hack to prevent locking up the browser if the backend (e.g. crossfilter) is too slow to respond to brushing events. You don't want those events backing up.

And that is probably what will happen if you try to connect this solution to other charts. So feel free not to accept this answer, but I did get something working.

Here we disable that delay in order to keep the sync of brushes from lagging, "at our peril".

Basically we can tie the filtered event to set the filter on the other charts. We also need to prevent a chain reaction, because each chart will in turn fire the filtered event.

const charts = [chart1,chart2,chart3,chart4];
let broadcasting = false; // don't repropogate (infinite loop)
for(const chartA of charts)
    chartA.on('filtered', function(chart, filter) {
        if(broadcasting)
            return;
        broadcasting = true;
        for(const chartB of charts.filter(chartB => chartB !== chartA))
            chartB.replaceFilter(filter);
        broadcasting = false;
    })

(Without the broadcasting flag, it will go into an infinite loop and crash the page.)

Again, this will probably run into performance problems when connected with other charts or real data. I am not sure if this can be done right without changing the library - the above issues describe what is needed.

sync brush Example fiddle: https://jsfiddle.net/gordonwoodhull/Lnz7c36e/19/