Search code examples
dc.jscrossfilter

d3-tip on dc-js chart disappears upon filtering over 0-valued group


I have a number of graphs, for simplicity take this example on jsfiddle, where there is a brush-on graph, a bar chart keyed on time and a row chart keyed on some categories. In addition, I use the d3-tip library for the tooltips (in the link above a very simplified version of my tip).

In order to avoid the creation of a bar-row in a rowChart, I used the fake-group as outlined in the FAQ of dc-js (and here as well).

The fake group works well, not displaying the C category on the row chart.

However, if I brush on some months with 0 data, when I reset the filter (just click anywhere but the filtered region on the brushon chart), the d3-tip on the row chart disappears.

Notice the if the group is created without the fake-grouping-function, this problem does not arise.

  • Any explanation why this happens?
  • How to avoid this (without loosing the remove_empty_bins)?

Solution

  • Although you can use dc.js and d3.js interchangeably, and dc.js is intentionally a "leaky abstraction", some things will go better if you do them the idiomatic dc.js way.

    I have two suggestions:

    1. Apply your tooltips in response to dc.js events so that they will get reapplied when new graphical objects are created (or re-created).
    2. Use chart.selectAll instead of d3.selectAll when modifying the charts.

    Okay, #2 actually has no bearing on this question, but it does help scope the selects better so that it's harder for them to miss the chart or accidentally modify stuff elsewhere in the page.

    Implementing #1 looks something like this:

    month_chart.on('pretransition', function(chart) {
      chart.selectAll('rect.bar').call(month_tip)
        .on('mouseover', month_tip.show).on('mouseout', month_tip.hide);
    });
    loc_chart1.on('pretransition', function(chart) {
      chart.selectAll('g.row').call(loc_tip)
        .on('mouseover', loc_tip.show).on('mouseout', loc_tip.hide);
    });
    

    The pretransition event fires right after a render or redraw, so it's usually the best moment to manipulate dc's elements. Much better than just running the code globally. I like to set everything up, then call dc.renderAll(), then allow the renders and redraws to take care of themselves later on.

    In particular, when those bars get added back in when remove_empty_bins stops removing them, these events will pick them up and re-tip them.

    Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/5feL3gko/4/