Search code examples
javascriptchartsdygraphs

Dygraphs: Highlight specific point on mouse over


Is there a way to highlight a specific point when mouse is over or near that specific point ? The thing is that I don't want to highlight all the lines but only the point(s) under or near my cursor. My goal is to show a tooltip at that position with the informations for that point.

This ChartJs example demonstrate pretty well what I would like to do: http://www.chartjs.org/samples/latest/scales/time/line.html

And these are my current options:

{
    drawPoints: true,
    showRoller: false,
    highlightCircleSize: 5,
    labels: ['Time', 'Vac', 'Temp'],
    ylabel: 'Vaccum (In/Hg)',
    y2label: 'Temperature ('+ TemperatureUnitFactory.getTemperatureUnit() + ')',
    series : {
        'Vac': {
          axis: 'y'
        },
        'Temp': {
          axis: 'y2'
        }
    },
    axes: {
        y: {
            drawGrid: true,
            independentTicks: true,
            valueRange: [0, -32],
            label: 'Vaccum'
        },
        y2: {
            drawGrid: false,
            independentTicks: true,
            valueRange: [
                TemperatureUnitFactory.getTemperatureForUnit(-30),
                TemperatureUnitFactory.getTemperatureForUnit(35)
            ],
            ylabel: 'Temperature'
        }
    }
}

If you feel like I am missing informations that would help you enlighting me, just let me know in a comment.

Thank you all!


Solution

  • So here's a snippet for the solution to my problem. I believe it could be optimized by throtling the mousemouve callback, but in my case it did just fine. I converted the snippet from angular to jQuery for "simplicity".

    var data = [
      [new Date(20070101),62,39],
      [new Date(20070102),62,44],
      [new Date(20070103),62,42],
      [new Date(20070104),57,45],
      [new Date(20070105),54,44],
      [new Date(20070106),55,36],
      [new Date(20070107),62,45],
      [new Date(20070108),66,48],
      [new Date(20070109),63,39],
      [new Date(20070110),57,37],
      [new Date(20070111),50,37],
      [new Date(20070112),48,35],
    ];
    
    var graph = new Dygraph(
      document.getElementById("chart"), data, {
        rollPeriod: 1,
        labels: ['Time', 'Vac', 'Temp'],
        showRoller: false,
        drawPoints: true,
      }
    );
    
    var tooltip = {
    	element: $('#tooltip'),
      x: function(_x){
      	this.element.css('left', _x);
      },
    	y: function(_y) {
      	this.element.css('top', _y);
      },
    	shown: false,
    	throttle: null,
    	currentPointData: null,
    	show: function() {
    		if(!this.shown) {
    			this.element.show();
          this.shown = true;
    		}
    	},
    	hide: function() {
    		this.cancelThrottle();
    		if(this.shown) {
    			this.element.hide();
          this.shown = false;
    		}
    	},
    	cancelThrottle: function () {
    		if(this.throttle !== null) {
    			clearTimeout(this.throttle);
    			this.throttle = null;
    		}
    	},
    	bindPoint: function (_point) {
      	this.element.html([_point.point.name,_point.point.xval, _point.point.yval].join(' | '))
    		console.log('Handle point data', _point);
    	}
    };
    
    var chartElement = $('#chart');
    var isMouseDown = false;
    
    chartElement.on('mousedown', function(){ isMouseDown = true; });
    chartElement.on('mouseup', function(){ isMouseDown = false; });
    
    chartElement.on('mousemove', function(){
    	if(graph === null) { return; }
    
      if(isMouseDown) {
        tooltip.hide();
        return;
      }
    
      const ACCEPTABLE_OFFSET_RANGE = 8;
      const TOOLTIP_BOTTOM_OFFSET = 25;
      const TOOLTIP_THROTTLE_DELAY = 600;
    
      var graphPos = Dygraph.findPos(graph.graphDiv),
        canvasX = Dygraph.pageX(event) - graphPos.x,
        canvasY = Dygraph.pageY(event) - graphPos.y,
        rows = graph.numRows(),
        cols = graph.numColumns(),
        axes = graph.numAxes(),
        diffX, diffY, xPos, yPos, inputTime, row, col, axe;
    
      for (row = 0; row < rows; row++)
      {
        inputTime = graph.getValue(row, 0);
        xPos = graph.toDomCoords(inputTime, null)[0];
        diffX = Math.abs(canvasX - xPos);
    
        if (diffX < ACCEPTABLE_OFFSET_RANGE)
        {
          for (col = 1; col < cols; col++)
          {
            var inputValue = graph.getValue(row, col);
            if (inputValue === null) { continue; }
    
            for(axe = 0; axe < axes; axe++)
            {
              yPos = graph.toDomCoords(null, inputValue, axe)[1];
              diffY = Math.abs(canvasY - yPos);
    
              if (diffY < ACCEPTABLE_OFFSET_RANGE)
              {
                tooltip.cancelThrottle();
    
                if(!tooltip.shown)
                {
                  var self = this;
    
                  tooltip.throttle = setTimeout(function () {
                    var ttHeight = tooltip.element.height(),
    										ttWidth = tooltip.element.width();
                    tooltip.x((xPos - (ttWidth / 2)));
                    tooltip.y((yPos - (ttHeight + TOOLTIP_BOTTOM_OFFSET)));
                    tooltip.show();
    
                    var closestPoint = graph.findClosestPoint(xPos, yPos);
                    if(closestPoint) {
                      tooltip.bindPoint(closestPoint);
                    }
    
                  }, TOOLTIP_THROTTLE_DELAY);
    
                }
    
                return;
              }
            }
          }
        }
      }
      tooltip.hide();
    });
    .chart-container {
      position:relative;
    }
    
    .chart-container > .tooltip {
      position:absolute;
      padding: 10px 10px;
      background-color:#ababab;
      color:#fff;
      display:none;
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.1.0/dygraph.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <div class="chart-container">
      <div id="chart"></div>
      <div id="tooltip" class="tooltip">
        Some data to be shown
      </div>
    </div>