Question

Example of Stacked Area Chart http://nvd3.org/ghpages/stackedArea.html

When I click on a series, it expands to all chart area, removing other series. How do I disable this functionality?

Was it helpful?

Solution

There isn't any NVD3 chart option to over-ride this behaviour, but you can over-ride the click event handler directly. However, it gets a little complicated with the stacked area chart...

NVD3 uses a d3.dispatch object to handle custom events. User clicks and mouseovers and related actions are all converted into these custom events.

If you want a function to happen after a custom event, you can call the dispatch object's .on(eventName, function) method. If the function parameter is null, it cancels out any previous event handling functions attached to that name. (If you want multiple functions to be triggered by the same event, you can add a "namespace" to the event name by using a string of the form "eventName.namespace" as the first parameter; that function will then only be cancelled if on is called again with the exact same event name string.)

So to cancel out a behaviour you don't want, you need to check the source code to figure out the name of the custom event that is triggering that behaviour, and call the dispatch object's on method with that name and a null function.

Here's where it gets complicated. There are actually multiple different events that cause a data series to be toggled on and off. If you click on the area, if you click on the legend, or if you click on one of the scatterpoint circles that appear when you mouseover, you get the same behaviour. So all of those events have to be over-written. And they're not even all part of the same dispatch object: The main chart object itself has a dispatch object that handles the complete redraw events created by clicking the chart layout controls, but the click events on the stacked areas are handled by an internal charting function that draws the plotting area, the click events on the scatterpoints are handled by an internal charting function within that, and the click events on the legend are handled within the legend function.

And here's where it gets really complicated. When the chart as a whole is updated or its layout changed, those internal chart-drawing functions for the plotting area and the scatterplot get over-written by the main chart function. Which means all the events get reset to their NVD3 defaults.

So not only do you have to disable all the events that trigger the behaviour, you also have to modify the update function to disable them all again. And because the update function itself is reset every update,* you need to include the modification of the update function as part of the function you use to disable the events.

**The update function just re-calls the entire chart drawing function using the same container selection. One of the first lines of the chart function creates the update function.*

Here's the code, based on the Stacked Area example on the nvd3.org live code page:

nv.addGraph(function() {

  /* Set up chart as normal */
  var chart = nv.models.stackedAreaChart()
                .x(function(d) { return d[0] })
                .y(function(d) { return d[1] })
                .clipEdge(true)
                //.useInteractiveGuideline(true)
      ;

  chart.xAxis
      .showMaxMin(false)
      .tickFormat(function(d) { return d3.time.format('%x')(new Date(d)) });

  chart.yAxis
      .tickFormat(d3.format(',.2f'));

  d3.select('#chart svg')
    .datum(data)
      .transition().duration(500).call(chart);

  /* create a function to disable events and modify the update function */
  function disableAreaClick() {

    //I'm probably over-killing with the amount of events I'm cancelling out
    //but it doesn't seem to have any side effects:
    chart.stacked.dispatch.on("areaClick", null);
    chart.stacked.dispatch.on("areaClick.toggle", null);

    chart.stacked.scatter.dispatch.on("elementClick", null);
    chart.stacked.scatter.dispatch.on("elementClick.area", null);

    chart.legend.dispatch.on("legendClick", null);
    chart.legend.dispatch.on("legendDblclick", null);
    chart.legend.dispatch.on("stateChange", null);

    if (chart.update) {
       //if the chart currently has an update function
       //(created when the chart is called on a container selection)
       //then modify it to re-call this function after update

       var originalUpdate = chart.update; 
           //assign the update function to a new name

       chart.update = function(){
          originalUpdate();
          disableAreaClick();
       }
    }
  } 

  //Call your function, disabling events on current chart and future updates:
  disableAreaClick();
  //this must be called *after* calling the chart on a selection
  //so that it has an update function to modify

  nv.utils.windowResize( chart.update );

  return chart;
});

OTHER TIPS

The best you can do for now is to disable the legend. You can do that using chart.showLegend(false), or, as an option like:

var options = {
    showLegend: false
};

A nasty solution if you don't mind not responding to any mouse event:

pointer-events: none;

Use this on the element.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top