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

<body>
  
  <div id="myDiv" style="width: 800px; height: 500px;">
    <!-- Plotly chart will be drawn inside this DIV -->
  </div>
  <script>
    <!-- JAVASCRIPT CODE GOES HERE -->
  </script>
</body>
var myPlot = document.getElementById('myDiv'),
    x = [1, 2, 3, 4, 5, 6],
    y = [1, 2, 3, 2, 3, 4],
    colors = ['#00000','#00000','#00000',
              '#00000','#00000','#00000'],
    data = [{x:x, y:y, type:'scatter',
             mode:'markers', marker:{size:16, color:colors}}],
    layout = {
      yaxis: {
        scaleanchor: 'x',
			  scaleratio: 1,
      },
      shapes: [{
          fillcolor: 'rgba(111, 231, 219, 0.4)',
          type: 'path',
          path: 'M 1.5 2.5 L 1.5 3.5 L 2.5 3.5 L 2.5 2.5 Z'   
        }],
      hovermode:'closest',
      title:'Drag the shape over a point to change its color'
    };

Plotly.newPlot('myDiv', data, layout, {editable: true});

var layerAbove = document.getElementsByClassName('layer-above')[0];
layerAbove.addEventListener('mousedown', (event) => { this.onMouseDownHandler(event.target); });

var currentDraggingShape;

this.startDragBehavior();

myPlot.on('plotly_click', function(data){
  console.log(data);
  var pn='',
      tn='',
      colors=[];
  for(var i=0; i < data.points.length; i++){
    pn = data.points[i].pointNumber;
    tn = data.points[i].curveNumber;
    colors = data.points[i].data.marker.color;
  };
  colors[pn] = '#C54C82';
    
  var update = {'marker':{color: colors, size:16}};
  Plotly.restyle('myDiv', update, [tn]);
});

function checkInsideRect(x0, x1, y0, y1, pX, pY) {
  if ((x0 <= pX && x1>= pX) && (y0 <= pY && y1>= pY)) {
    return true;
  }
  
  return false;
}


function startDragBehavior() {
  var drag = Plotly.d3.behavior.drag();
  var minX, maxX, minY, maxY, deltaX, deltaY, lastDragPoint;  
  const graphElement = document.getElementById('myDiv');
  
  drag.on('dragstart', function (e) {
    lastDragPoint = getCurrentMousePositionOnCS(event['clientX'], event['clientY'], graphElement);
    
  });
  
  drag.on('drag', function (e) {
    if (currentDraggingShape) {
      const currentDragPoint = getCurrentMousePositionOnCS(event['clientX'], event['clientY'], graphElement);
      
      deltaX = currentDragPoint.x - lastDragPoint.x;
			deltaY = currentDragPoint.y - lastDragPoint.y;
      
      const minX = currentDraggingShape._extremes.x.min[0].val + deltaX;
      const maxX = currentDraggingShape._extremes.x.max[0].val + deltaX;
      const minY = currentDraggingShape._extremes.y.min[0].val + deltaY;
      const maxY = currentDraggingShape._extremes.y.max[0].val + deltaY;
      
      var isInside = false;
      for(var i=0; i < graphElement.data[0].x.length; i++){
        var pX = graphElement.data[0].x[i];
        var pY = graphElement.data[0].y[i];
        
        isInside = checkInsideRect(minX, maxX, minY, maxY, pX, pY);
        if (isInside) {
          break;
        }
      };
      
      var colorArray = ['#00000','#00000','#00000', '#00000','#00000','#00000'];
      
      var update;
      if (isInside) {
        colorArray[i] = 'red';  
      }
      
      update = {'marker':{color: colorArray, size:16}};
      
      Plotly.restyle('myDiv', update);
    }
    
  });
  
  drag.on('dragend', function (e) {
    currentDraggingShape = null;
  });
  
  Plotly.d3.selectAll('[id=\'' + 'myDiv' + '\']').call(drag);
}

function getCurrentMousePositionOnCS(clientX, clientY, graphElement) {
		const clientRect = graphElement.getBoundingClientRect();
		const d3X = clientX - clientRect.left;
		const d3Y = clientY - clientRect.top;
    const left = graphElement._fullLayout.margin.l;
    const top = graphElement._fullLayout.margin.t
		const currentDragX = graphElement._fullLayout.xaxis.p2c(d3X- left);
		const currentDragY = graphElement._fullLayout.yaxis.p2c(d3Y - top);

		return {x: currentDragX, y: currentDragY};
	}

function onMouseDownHandler(shapeElement) {
		const shapeElements = document.getElementsByClassName('layer-above')[0].
								getElementsByClassName('shapelayer')[0].getElementsByTagName('path');

		let shapeIndex = -1;
		for (let i = 0; i < shapeElements.length; i++) {
			const shape = shapeElements[i];
			if (shape === shapeElement) {
				shapeIndex = i;
				break;
			}
		}

		currentDraggingShape = document.getElementById('myDiv')._fullLayout.shapes[shapeIndex];
}

Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.plot.ly/plotly-latest.min.js