Spikes are hidden when there is no data

Another one about spikes, as you can see in the gif the spikes disappear if there are no traces and I would like them to remain visible.

spikes

In order to have the spikes always visible, I had to resort to modifying the Plotly.js file again. Other options were that instead of making the graph invisible, I could change its opacity or its line width to 0 or have a second graph with these properties, but neither of the two options seemed good or optimal to me, so while reviewing the code I found the createSpikeline function and this is how it looks after modifying it.

function createSpikelines(gd, closestPoints, opts) {
  var container = opts.container;
  var fullLayout = opts.fullLayout;
  var gs = fullLayout._size;
  var evt = opts.event;
  var showY = !!closestPoints.hLinePoint;
  var showX = !!closestPoints.vLinePoint;
  var xa, ya;
  var subplot = evt.target.attributes["data-subplot"].value
  // Remove old spikeline items
  container.selectAll('.spikeline').remove();
  // if (!(showX || showY)) return;
  
  var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor);

  // Horizontal line (to y-axis)
  if (showY) {
    var hLinePoint = closestPoints.hLinePoint;
    var hLinePointX, hLinePointY;
    xa = hLinePoint && hLinePoint.xa;
    ya = hLinePoint && hLinePoint.ya;
    var ySnap = ya.spikesnap;
    if (ySnap === 'cursor') {
      hLinePointX = evt.pointerX;
      hLinePointY = evt.pointerY;
    } else {
      hLinePointX = xa._offset + hLinePoint.x;
      hLinePointY = ya._offset + hLinePoint.y;
    }
    var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ? Color.contrast(contrastColor) : hLinePoint.color;
    var yMode = ya.spikemode;
    var yThickness = ya.spikethickness;
    var yColor = ya.spikecolor || dfltHLineColor;
    var xEdge = Axes.getPxPosition(gd, ya);
    var xBase, xEndSpike;
    if (yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) {
      if (yMode.indexOf('toaxis') !== -1) {
        xBase = xEdge;
        xEndSpike = hLinePointX;
      }
      if (yMode.indexOf('across') !== -1) {
        var xAcross0 = ya._counterDomainMin;
        var xAcross1 = ya._counterDomainMax;
        if (ya.anchor === 'free') {
          xAcross0 = Math.min(xAcross0, ya.position);
          xAcross1 = Math.max(xAcross1, ya.position);
        }
        xBase = gs.l + xAcross0 * gs.w;
        xEndSpike = gs.l + xAcross1 * gs.w;
      }

      // Foreground horizontal line (to y-axis)
      container.insert('line', ':first-child').attr({
        x1: xBase,
        x2: xEndSpike,
        y1: hLinePointY,
        y2: hLinePointY,
        'stroke-width': yThickness,
        stroke: yColor,
        'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness)
      }).classed('spikeline', true).classed('crisp', true);

      // Background horizontal Line (to y-axis)
      // container.insert('line', ':first-child').attr({
      //   x1: xBase,
      //   x2: xEndSpike,
      //   y1: hLinePointY,
      //   y2: hLinePointY,
      //   'stroke-width': yThickness + 2,
      //   stroke: contrastColor
      // }).classed('spikeline', true).classed('crisp', true);
    }
    // Y axis marker
    if (yMode.indexOf('marker') !== -1) {
      container.insert('circle', ':first-child').attr({
        cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness),
        cy: hLinePointY,
        r: yThickness,
        fill: yColor
      }).classed('spikeline', true);
    }
  }
  else {
    ya = fullLayout._plots[subplot].yaxis;

    var xEdge = Axes.getPxPosition(gd, ya);
    var xBase, xEndSpike;
    if (ya.spikemode.indexOf('toaxis') !== -1 || ya.spikemode.indexOf('across') !== -1) {
      if (ya.spikemode.indexOf('toaxis') !== -1) {
        xBase = xEdge;
        xEndSpike = evt.pointerX;
      }
      if (ya.spikemode.indexOf('across') !== -1) {
        var xAcross0 = ya._counterDomainMin;
        var xAcross1 = ya._counterDomainMax;
        if (ya.anchor === 'free') {
          xAcross0 = Math.min(xAcross0, ya.position);
          xAcross1 = Math.max(xAcross1, ya.position);
        }
        xBase = gs.l + xAcross0 * gs.w;
        xEndSpike = gs.l + xAcross1 * gs.w;
      }

      // Foreground horizontal line (to y-axis)
      container.insert('line', ':first-child').attr({
        x1: xBase,
        x2: xEndSpike,
        y1: evt.pointerY,
        y2: evt.pointerY,
        'stroke-width': ya.spikethickness,
        stroke: ya.spikecolor,
        'stroke-dasharray': Drawing.dashStyle(ya.spikedash, ya.spikethickness)
      }).classed('spikeline', true).classed('crisp', true);
    }

    if (ya.spikemode.indexOf('marker') !== -1) {
      container.insert('circle', ':first-child').attr({
        cx: xEdge + (ya.side !== 'right' ? ya.spikethickness : -ya.spikethickness),
        cy: evt.pointerY,
        r: ya.spikethickness,
        fill: ya.spikecolor
      }).classed('spikeline', true);
    }
  }
  if (showX) {
    var vLinePoint = closestPoints.vLinePoint;
    var vLinePointX, vLinePointY;
    xa = vLinePoint && vLinePoint.xa;
    ya = vLinePoint && vLinePoint.ya;
    var xSnap = xa.spikesnap;
    if (xSnap === 'cursor') {
      vLinePointX = evt.pointerX;
      vLinePointY = evt.pointerY;
    } else {
      vLinePointX = xa._offset + vLinePoint.x;
      vLinePointY = ya._offset + vLinePoint.y;
    }
    var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ? Color.contrast(contrastColor) : vLinePoint.color;
    var xMode = xa.spikemode;
    var xThickness = xa.spikethickness;
    var xColor = xa.spikecolor || dfltVLineColor;
    var yEdge = Axes.getPxPosition(gd, xa);
    var yBase, yEndSpike;
    if (xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) {
      if (xMode.indexOf('toaxis') !== -1) {
        yBase = yEdge;
        yEndSpike = vLinePointY;
      }
      if (xMode.indexOf('across') !== -1) {
        var yAcross0 = xa._counterDomainMin;
        var yAcross1 = xa._counterDomainMax;
        if (xa.anchor === 'free') {
          yAcross0 = Math.min(yAcross0, xa.position);
          yAcross1 = Math.max(yAcross1, xa.position);
        }
        yBase = gs.t + (1 - yAcross1) * gs.h;
        yEndSpike = gs.t + (1 - yAcross0) * gs.h;
      }

      // Foreground vertical line (to x-axis)
      container.insert('line', ':first-child').attr({
        x1: vLinePointX,
        x2: vLinePointX,
        y1: yBase,
        y2: yEndSpike,
        'stroke-width': xThickness,
        stroke: xColor,
        'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness)
      }).classed('spikeline', true).classed('crisp', true);

      // Background vertical line (to x-axis)
      // container.insert('line', ':first-child').attr({
      //   x1: vLinePointX,
      //   x2: vLinePointX,
      //   y1: yBase,
      //   y2: yEndSpike,
      //   'stroke-width': xThickness + 2,
      //   stroke: contrastColor
      // }).classed('spikeline', true).classed('crisp', true);
    }

    // X axis marker
    if (xMode.indexOf('marker') !== -1) {
      container.insert('circle', ':first-child').attr({
        cx: vLinePointX,
        cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness),
        r: xThickness,
        fill: xColor
      }).classed('spikeline', true);
    }
  }
  else {
    xa = fullLayout._plots[subplot].xaxis

    var yEdge = Axes.getPxPosition(gd, xa);
    var yBase, yEndSpike;
    if (xa.spikemode.indexOf('toaxis') !== -1 || xa.spikemode.indexOf('across') !== -1) {
      if (xa.spikemode.indexOf('toaxis') !== -1) {
        yBase = yEdge;
        yEndSpike = evt.pointerY;
      }
      if (xa.spikemode.indexOf('across') !== -1) {
        var yAcross0 = xa._counterDomainMin;
        var yAcross1 = xa._counterDomainMax;
        if (xa.anchor === 'free') {
          yAcross0 = Math.min(yAcross0, xa.position);
          yAcross1 = Math.max(yAcross1, xa.position);
        }
        yBase = gs.t + (1 - yAcross1) * gs.h;
        yEndSpike = gs.t + (1 - yAcross0) * gs.h;
      }

      // Foreground vertical line (to x-axis)
      container.insert('line', ':first-child').attr({
        x1: evt.pointerX,
        x2: evt.pointerX,
        y1: yBase,
        y2: yEndSpike,
        'stroke-width': xa.spikethickness,
        stroke: xa.spikecolor,
        'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xa.spikethickness)
      }).classed('spikeline', true).classed('crisp', true);
    }
    
    // X axis marker
    if (xa.spikemode.indexOf('marker') !== -1) {
      container.insert('circle', ':first-child').attr({
        cx: evt.pointerX,
        cy: yEdge - (xa.side !== 'top' ? xa.spikethickness : -xa.spikethickness),
        r: xa.spikethickness,
        fill: xa.spikecolor
      }).classed('spikeline', true);
    }
  }
}

In addition to changing the condition that is in the first call to the function

if (hasCartesian && (spikePoints.hLinePoint !== null || spikePoints.vLinePoint !== null)) 

for

if (hasCartesian) 

And this is the result

spikes