6

I am trying to implement a 'click anywhere' feature in plotly so that I can get the coordinates on user clicks anywhere on plotly charts. The current "official" plotly functionality only works when users click on a plotted data point, but I want to register clicks e.g. on the background white canvas.

Shiny click events for plots can do that, but surprisingly this doesn't seem to exist yet in plotly.

I made some research and found the following codepen implementation which comes close: https://codepen.io/tim-logan/pen/yLXgpyp

#JS
var d3 = Plotly.d3;
var gd = document.getElementById('graph');

Plotly.plot('graph', [{
  y: [2, 1, 2]
}])
.then(attach);

function attach() {
  var xaxis = gd._fullLayout.xaxis;
  var yaxis = gd._fullLayout.yaxis;
  var l = gd._fullLayout.margin.l;
  var t = gd._fullLayout.margin.t;
  
  gd.addEventListener('mousemove', function(evt) {
    var xInDataCoord = xaxis.p2c(evt.x - l);
    var yInDataCoord = yaxis.p2c(evt.y - t);
    console.log(evt.x, l)
    
    Plotly.relayout(gd, 'title', ['x: ' + xInDataCoord, 'y : ' + yInDataCoord].join('<br>'));
  });
}

# HTML
<head>
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
  <div style="padding-left: 100px; margin-left: 100px">
    <div id="graph"></div>
  </div>
</body>

However, as the creator of the codepen pointed out, the click coordinates are not precises as they don't take into account the padding / margin:

One issue with the example workaround is that it doesn't account for padding/margin in parent objects. I've taken the example and added a parent div that has both padding and margin on the left side, this then skews the value of the x-axis value, and is clearly something that would need to be handled in such a solution as is suggested here.

According to the flot documentation this should be possible for plot objects by subtracting the plot offset from the coordinates: https://github.com/flot/flot/blob/master/API.md#plot-methods

See e.g. the following excerpt:

Various things are stuffed inside an axis object, e.g. you could use getAxes().xaxis.ticks to find out what the ticks are for the xaxis. Two other useful attributes are p2c and c2p, functions for transforming from data point space to the canvas plot space and back. Both returns values that are offset with the plot offset.

or:

offset()

Returns the offset of the plotting area inside the grid relative to the document, useful for instance for calculating mouse positions (event.pageX/Y minus this offset is the pixel position inside the plot).

I tried to implement a workaround based on offset(), but since my js knownledge is not too good I couldn't get a working version of the code.

Would somebody be able to provide a workaround to fix the offset problem?

Ronak Shah
  • 377,200
  • 20
  • 156
  • 213

1 Answers1

2

I managed to remove the offset by getting the parent box's dimensions. See following example which fixed the above codepen:

var d3 = Plotly.d3;
var gd = document.getElementById('graph');

Plotly.plot('graph', [{
  y: [2, 1, 2]
}])
.then(attach);

function attach() {
  
  gd.addEventListener('mousemove', function(evt) {
    var bb = evt.target.getBoundingClientRect();
    var x = gd._fullLayout.xaxis.p2d(evt.clientX - bb.left);
    var y = gd._fullLayout.yaxis.p2d(evt.clientY - bb.top);
    
    Plotly.relayout(gd, 'title', ['x: ' + x, 'y : ' + y].join('<br>'));
  });
}

Fixed codepen here: https://codepen.io/flaviofusero/pen/BaZRgzO

Adapted from sleighsoft's implementation here: plotly click events from anywhere on the plot