Search code examples
plotlyplotly.js

[Shape click event][Active shape index] How to retrieve the index of a drawn shape, on which user clicked on, using an event?


I need to draw rectangles using the ‘drawrect’ button on a heatmap and obtain the index of the active shape when it is clicked on.

Currently, the plotly_click event does not trigger when I click on the shape outline, and the plotly_relayout event only triggers when the shape is dragged, resized, or erased.

A simple example is available here codepenExample or here:

HTML:

<head>
  <!-- Plotly.js -->
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>

<body>
<div id="graph"></div>
</body>

JS:

var shapes = [
  {
    editable: true,
    label: { text: "" },
    xref: "x",
    yref: "y",
    layer: "above",
    opacity: 1,
    line: { color: "white", width: 5, dash: "solid" },
    fillcolor: "rgba(0, 0, 0, 0)",
    fillrule: "evenodd",
    type: "rect",
    x0: 0.5,
    y0: 0,
    x1: 0.75,
    y1: 1.5
  }
];

var fig = {
  data: [
    {
      z: [
        [1, 20, 30],
        [20, 1, 60],
        [30, 60, 1]
      ],
      type: "heatmap"
    }
  ],
  layout: {
    title: "Heatmap with drawn rectangles",
    shapes: shapes,
    dragmode: "drawrect",
    clickmode: "event+select"
  },
  config: {
    displayModeBar: true,
    modeBarButtons: [[], ["zoom2d", "drawrect", "eraseshape", "autoScale2d"]]
  }
};

var gd = document.getElementById("graph");

Plotly.plot(gd, fig);

gd.on("plotly_click", function (d) {
  alert("plotly click");
  console.log(d);
});

gd.on("plotly_relayouting", function (d) {
  alert("plotly relayouting");
  console.log(d);
});

// gd.on("plotly_relayout", function(d) {
//   alert("plotly relayout")
//   console.log(d)
// })

// gd.on("plotly_selected", function(d) {
//   alert("plotly selected")
//   console.log(d);
// });

// gd.on("plotly_selecting", function(d) {
//   alert("plotly selecting")
//   console.log(d);
// });

I have looked into the draw.js file of the plotly.js source code (draw.js) and found the function that is automatically triggered when a shape is clicked on to change its style and make it draggable, resizable, or erasable (line 189):

path.node().addEventListener('click', function() { return activateShape(gd, path); });

Therefore, I believe there should be a way to add actions to this ‘click’ event and obtain its index as well.

I have also read about some dependencies with the dragmode and clickmode attributes’ values, but I am unable to proceed with them.

Thanks a lot for your help! Yaya


Solution

  • I've looked for the function activateShape(gd, path) it in the plotly library, and added console.log for all the arguments:

    function activateShape(gd, path) {
      if (!couldHaveActiveShape(gd)) return;
      var element = path.node();
      var id = +element.getAttribute('data-index');
      if (id >= 0) {
        // deactivate if already active
        if (id === gd._fullLayout._activeShapeIndex) {
          deactivateShape(gd);
          return;
        }
        gd._fullLayout._activeShapeIndex = id;
        gd._fullLayout._deactivateShape = deactivateShape;
        draw(gd);
        console.log(gd, path, element, id);
      }
    }
    

    The id is the index of the shape.

    Replacing the console.log() with the following:

    gd.emit('plotly_relayout', {"shape": gd._fullLayout.shapes[id]});
    

    Now you will have a plotly_relayout event fired with the active shape as an argument. To catch this event you can use something like the following code:

    div.on('plotly_relayout', function (eventData) {
        console.log('plotly_relayout:');
        if (eventData.hasOwnProperty('shape')) {
            console.log(true);
            alert(eventData.shape.x0 + ', ' + eventData.shape.x1 + ', ' + eventData.shape.y0 + ', ' + eventData.shape.y1);
        } else {
            console.log(false);
        }
        console.log(eventData);
    }
    )