1
votes

I am trying to make just the axis label of the selected item bold, so that it is more apparent to users what they have selected. I am using a composite bar chart to compare two values and have turned the labels -90 so that they are inside the bars. I have been able to make the labels clickable, with help from Gordon here: dc.js barChart - click on x-axis ticks and labels

I am counting clicks so that users can select and deselect from clicking on the label, however with some of the smaller values, users cannot tell which items they have filtered on in any specific chart.

I am able to select, de-select on clicks, I have tried select('tick-text').attr('style', 'font-weight: bold;');

This bolds the first item in the x-axis, no matter which selection is made.

When I use selectAll('.x text) it changes all.

Relevant portion of the code:

        ByTopLvl.on('pretransition',function() {                            
             ByTopLvl.selectAll('g.x text')
             .style('text-anchor', 'start')                                
             .attr('transform', 'rotate(-90),translate(10, -10)'); 

        ByTopLvl.select('.axis.x')
             .selectAll('.tick text')                                
             .on('click.custom', function (d) {
                  var clicks = $(this).data('clicks');
                  if (!clicks) {                                        
                       ByTopLvl.replaceFilter(d)                                           
                       .select('.tick text')
                       .attr('style', 'font-weight: bold;');

                       ByTopLvl.redrawGroup();
              } else {
                       ByTopLvl.select('.tick text')
                       .attr('style', 'font-weight: normal;');

                       ByTopLvl.filterAll();
                       dc.redrawAll();
         }

         $(this).data("clicks", !clicks);
      });

I would expect that when I click on the label, only the label clicked would be bold.

1
Hi, You are not using the element on your select (you don't use d). select takes the first element (the first tick). I'm not sure what d is exactly, but you need to use it somehow in your selectXavier

1 Answers

1
votes

First off, whenever possible, I would suggest using the built-in selection/filter state rather than trying to keep track of clicks yourself. Otherwise they are bound to get out of sync at some point.

If you have the boldness of the ticks driven by the active filters, then you'll get the same behavior whether the bar or the tick was clicked, and you can be certain that exactly the ticks in the filters are the bolded ones:

CSS

.dc-chart g.axis.x text.selected {
  font-weight: bold;
}

JS

chart.on('filtered', function(chart) {
  var filters = chart.filters();
  chart.selectAll('.axis.x .tick text').classed('selected', function(d) {
    return filters.includes(d);
  })
})

[Side note since I'm not answering your exact question: if you want to make your code work, you could do something like filter the selection based on d:

ByTopLvl.select('.tick text').filter(function(d2) { return d2 === d; })

or in your case, this is the clicked tick, so d3.select(this) should also work. But I think you'll run into a lot of bugs that way.]

Similarly, you can simplify your click behavior by tying into the built-in filter behavior, which already toggles:

chart.on('pretransition', function(chart) {
  chart.select('.axis.x')
      .selectAll('.tick text')
      .on('click.select', function(d) {
          chart.filter(d); 
          chart.redrawGroup();
      });
});

Yeah, it's weird that the built-in filter function toggles, but that's just the way that dc.js evolved.

demo screenshot

Here's a demo fiddle.

In a composite

Composite charts in dc.js are a little bit of a confusing mess.

The filter selection is shared between the parent and child, except they sort also handle it separately.

Unfortunately when I have to troubleshoot composite charts, I just try different things until it works, and never fully understand what's going on. I think it's just too complicated for my brain. :-O

Anyway, this worked...

Keep a reference to the inner bar chart:

var chart = dc.compositeChart('#test'), bar;
  chart
      .width(768)
      .height(380)
      .x(d3.scaleBand())
      .xUnits(dc.units.ordinal)
      .brushOn(false)
      .xAxisLabel('Fruit')
      .yAxisLabel('Quantity Sold')
      .dimension(fruitDimension)
      .group(sumGroup)
      ._rangeBandPadding(0.05)
      .compose([
      bar = dc.barChart(chart)
          .barPadding(0.1)
          .outerPadding(0.05)
      ])

When responding to the click, filter the child bar chart instead of the parent:

chart.on('pretransition', function(chart) {
  chart.select('.axis.x')
      .selectAll('.tick text')
      .on('click.select', function(d) {
          bar.filter(d); 
          chart.redrawGroup();
      });
});

Listen to the filtered event of the child bar chart and apply axis bolding to the parent composite chart:

bar.on('filtered', function(chart) {
  var filters = chart.filters();
  chart.selectAll('.axis.x .tick text').classed('selected', function(d) {
    return filters.includes(d);
  })
})

Whoo. I guess it's sorta.. consistent? The child bar chart is the source of truth for the filters. Maybe I'm rationalizing.

New fiddle version.