sudobangbang
sudobangbang

Reputation: 251

NVD3 Line Chart -- Overlay Points with Independent Events

I am using nvd3's simple line chart for a project. There is a need for users to be able to enter X values in a modal form and annotate the graph with a point. Their entries will not update the lines in the chart, just the point annotations.

I've tried to tackle this by creating a group container for my annotation points above the <rect> element. The <rect> element for the line chart in nvd3 seems to be the layer that captures all events and delegates, but i don't want to use this to delegate to my annotation points.

var annotation_container = d3.select( '.nvd3.nv-wrap.nv-lineChart > g' )
                              .insert( "g", ":first-child" )
                              .attr( "id", "nv-annotate-container" );

var annotation_groups = annotation_container.selectAll( "g" ).data( data );

annotation_groups
  .enter()
  .append( "g" )
  .attr( "id", function( d ){ return d.key; } )
  .append( "g" )
  .attr( "id" , "popup-box" )
  .style( "fill", "#0000ff" )
  .style( "opacity",  "0.8" )
  .attr( "transform", "matrix(0.17443456,0,0,0.2187901,0.55739522,0.48858108)" )
  .append( "path" )
  .attr( "id", "popup-box-path" )
  .attr( "d", "m 99.999,68.332 c 0,5.5 -4.5,10 -10,10 H 10 c -5.5,0 -10,-4.5 -10,-10 V 10 C 0,4.5 4.5,0 10,0 h 79.999 c 5.5,0 10,4.5 10,10 v 58.332 z" );

annotation_groups.on( 'click', function( e ) {
        console.log( "CLICK ", e );
    });

The problem is that after creating the annotation group, none of the events i try to use ( mouseover, mouseout, click etc ) work.

Questions:

  1. How do I get my events to work?
  2. Is there a better way to achieve what I'm trying to do?
  3. Has anyone tried this before and got a working implementation?

Thanks

Upvotes: 0

Views: 1316

Answers (2)

Valchris
Valchris

Reputation: 1481

Camilles answer didn't quite work for me, I suspect I have a different version of nvd3. It did however help me figure out what I needed to do. I deleted the if block at the top altogether and the selector at the top, was invalid. Instead I used:

vertLines = d3.select('.nvd3 .nv-linesWrap')
    .select('.nv-groups')
    .selectAll('.nv-groups')
    .data(chartVersions)

Upvotes: 0

Camille R
Camille R

Reputation: 1433

Yes it's possible,

Here is an example of how to do it : https://gist.github.com/timelyportfolio/80d85f78a5a975fa29d7#file-code-r

The solution here is to add a javascript function drawing vertical lines using NVD3 ( read carefully the comments ) :

function drawVerticalLines(opts) {

  // CAREFUL HERE !!! the css pasth ".nvd3 .nv-focus .nv-linesWrap" depends on the type of chart you are using, lineChart would use only ".nvd3 .nv-linesWrap" ... !
  if (!(d3.select('#' + opts.id + ' the css pasth ".nvd3 .nv-focus .nv" depends on the type of chart you are using, lineChart would use only -linesWrap').select('.vertical-lines')[0][0])) {
    // Adds new g element with .vertical-lines class; use a css debugger to verify
    d3.select('#' + opts.id + ' .nvd3 .nv-focus .nv-linesWrap').append('g')
      .attr('class', 'vertical-lines')
  }

  vertLines = d3.select('#' + opts.id + ' .nvd3 .nv-focus .nv-linesWrap').select('.vertical-lines').selectAll('.vertical-line')
    .data(
      [{
        'date': new Date('1967-11-30'),
        'label': 'something to highlight 1967'
      }, {
        'date': new Date('2001-11-30'),
        'label': 'something to highlight 2001'
      }])

  var vertG = vertLines.enter()
    .append('g')
    .attr('class', 'vertical-line')

  vertG.append('svg:line')
  vertG.append('text')

  vertLines.exit().remove()

  // CAREFUL 2 : chart.xAxis.scale() scale depends how you are defining your x Axis in nvd3 chart ... if your are using timestamps, (d.date / 60 / 60 / 24 / 1000) becomes (d.date)

  vertLines.selectAll('line')
    .attr('x1', function(d) {
      return chart.xAxis.scale()(d.date / 60 / 60 / 24 / 1000)
    })
    .attr('x2', function(d) {
      return chart.xAxis.scale()(d.date / 60 / 60 / 24 / 1000)
    })
    .attr('y1', chart.yAxis.scale().range()[0])
    .attr('y2', chart.yAxis.scale().range()[1])
    .style('stroke', 'red')

  vertLines.selectAll('text')
    .text(function(d) {
      return d.label
    })
    .attr('dy', '1em')
    //x placement ; change dy above for minor adjustments but mainly
    //    change the d.date/60/60/24/1000 
    //y placement ; change 2 to where you want vertical placement
    //rotate -90 but feel free to change to what you would like
    .attr('transform', function(d) {
      return 'translate(' +
        chart.xAxis.scale()(d.date / 60 / 60 / 24 / 1000) +
        ',' +
        chart.yAxis.scale()(2) +
        ') rotate(-90)'
    })
    //also you can style however you would like
    //here is an example changing the font size
    .style('font-size', '80%')

}

And call this method in nv.addGraph Callback :

var sharedChart = null; // Shared reference on the chart

nv.addGraph(function() {
      .....

      sharedChart = chart;

      return chart;

      ,
      function() {
        drawVerticalLines(opts, sharedChart);
      }
    );

With opts ... (obviously you don't really need it):

var opts${widgetID.replace('-', '0')} = {
         "dom": "chart${widgetID}",
         "width":    800,
         "height":    400,
         "x": "date",
         "y": "value",
         "group": "variable",
         "type": "lineWithFocusChart",
         "id": "chart${widgetID}" 
     };

Hope this helps, it took me quite a long time to find it and to make it work !

Upvotes: 1

Related Questions