1

Could anyone tell me how to customize the brush to display filter values as labels?

I would like to get the same style as the one marked with arrows in the following screenshot but I don't know how to get it, and I haven't seen any example.

Custom Brush filter example

Gordon
  • 19,811
  • 4
  • 36
  • 74
Tolo Mir
  • 23
  • 3
  • Wow.. thank you so much for your advices and very grateful for editing my question! It's my first question in SO and for sure I'll follow your tips! Thanks again! ;) – Tolo Mir Feb 10 '20 at 12:15

1 Answers1

2

This is a great question, and I am surprised no one has asked this before. Obviously, dc.js can display the filter values in the text above the chart, but putting it right on the brush is really cool!

Any dc.js chart will allow you to listen for the pretransition event and draw your own annotations using D3.

Let's do that:

  chart.on('pretransition', function(chart) {
    let brushBegin = [], brushEnd = []; // 1
    if(chart.filter()) {
      brushBegin = [chart.filter()[0]]; // 2
      brushEnd = [chart.filter()[1]];
    }
    let beginLabel = chart.select('g.brush') // 3
      .selectAll('text.brush-begin')
      .data(brushBegin); // 4
    beginLabel.exit().remove(); // 5
    beginLabel = beginLabel.enter()
      .append('text') // 6
      .attr('class', 'brush-begin') // 7
      .attr('text-anchor', 'end')
      .attr('dominant-baseline', 'text-top')
      .attr('fill', 'black')
      .attr('y', chart.margins().top)
      .attr('dy', 4)
      .merge(beginLabel); // 8
    beginLabel
      .attr('x', d => chart.x()(d))
      .text(d => d.toFixed(2)); // 9
    let endLabel = chart.select('g.brush')
      .selectAll('text.brush-end')
      .data(brushEnd);
    endLabel.exit().remove();
    endLabel = endLabel.enter()
      .append('text')
      .attr('class', 'brush-end')
      .attr('text-anchor', 'begin')
      .attr('dominant-baseline', 'text-top')
      .attr('fill', 'black')
      .attr('y', chart.margins().top)
      .attr('dy', 4)
      .merge(endLabel);
    endLabel
      .attr('x', d => chart.x()(d))
      .text(d => d.toFixed(2));
  })

This looks like a lot of code; it's really doing the same thing twice, once for each label. Let's look at how the first label is shown.

  1. D3 binds array data to elements. We will bind labels at the beginning and end of the brush each to an array of zero (brush hidden) or one element (brush shown). This line defaults the arrays to empty.
  2. If there is a filter active, we will set the arrays to one-element arrays containing the begin and end values of the brush.
  3. Standard D3 boilerplate: select the parent element (d.brush), then select the elements we want to create, update, or destroy, then
  4. Bind the zero- or one-element arrays to the selections
  5. If the brush was just hidden, delete the label
  6. If the brush was just shown, add the label
  7. and initialize it with the needed SVG attributes and the class brush-begin which we just used in binding. Most of these attributes are to get the label position correct.
  8. Merge the selections together, so now we have an insert+modify selection
  9. Apply the X position attribute and change the text change whenever the brush changes.

screenshot of brush with begin/end labels

https://jsfiddle.net/gordonwoodhull/w4xhv8na/33/

Getting white-on-black labels is actually not trivial but I hope to return to that later.

Gordon
  • 19,811
  • 4
  • 36
  • 74
  • This is a very good beginning @Gordon! Getting white-on-black could be through svg objects rather than css. I'll try it! Thank you so much! – Tolo Mir Feb 12 '20 at 18:36
  • I'm wondering where I can get a dc.js grouped bar chart that uses a timeline brush! Maybe this could be another feed but maybe it is s just an example link! Thank you again! – Tolo Mir Feb 12 '20 at 18:38
  • Sigh, it's a reasonable question. There are [two](https://github.com/dc-js/dc.js/pull/1470) [competing](https://github.com/dc-js/dc.js/pull/1459) implementations, but I haven't wanted to merge either of them because each one messes up the codebase in a different way. If you're using DCv3, one of them [shows how to](https://github.com/dc-js/dc.js/pull/1459#issuecomment-403884323) pull a fairly new branch of dc with the patch. It's a painful topic for me. – Gordon Feb 12 '20 at 19:13
  • I was looking for this for weeks :) later i'll test it, but for now...THANKS :D – lbrutti Feb 22 '21 at 11:25