Fill / Shade a chart above a specific Y value in PlotlyJS

Here is another solution exploiting Plotly's fill: "toself". The idea is to create a closed line trace which encloses the area above the threshold and the markers of the main line. Works for threshold values above zero and for numerical x-values.

enter image description here The helper traces have their legend hidden and are grouped with the main trace, thereby preventing ugly artifacts when toggling the legend.

The function checks for each x-y-pair if the y-value is above the threshold, if yes

  • check if there is already a segment above the threshold and use this one OR create a new sgement
  • the segement starts from the y-value of the threshold and the intermediate x-value from the point above the threshold and the one before.
  • each segment is terminated with an y-value which is equal to the threshol and the x-value which the mean of the last point in the segment and the next one

The function itself can be surely written in a nicer way but it's just a proof-of-concept.

function dataToTraces(data, threshold) {

  var fillers = [];

  var emptyFiller = {
    x: [],
    y: [],
    fill: "toself",
    mode: "lines",
    line: {
      width: 0
    },
    opacity: 0.5,
    fillcolor: "#8adcb3",
    showlegend: false,
    legendgroup: "main"
  }
  
  fillers.push(emptyFiller);

  for (var i = 0; i < data.y.length; i += 1) {
    if (data.y[i] >= threshold) {
      if (i !== 0 && data.y[i - 1] < threshold) {
        fillers[fillers.length - 1].x.push(data.x[i - 1] + (threshold - data.y[i - 1]) / (data.y[i] - data.y[i - 1]));
        fillers[fillers.length - 1].y.push(threshold);
      }
      fillers[fillers.length - 1].x.push(data.x[i]);
      fillers[fillers.length - 1].y.push(data.y[i]);
    } else if (fillers[fillers.length - 1].x.length > 0) {
      if (i !== 0 && data.y[i - 1] !== threshold) {
        fillers[fillers.length - 1].x.push(data.x[i - 1] + (threshold - data.y[i - 1]) / (data.y[i] - data.y[i - 1]));
        fillers[fillers.length - 1].y.push(threshold);
      }
      fillers.push(emptyFiller);
    }
  }
  return fillers;
}

var data = [{
  x: [0, 1, 2, 3, 4, 5, 6, 7, 8],
  y: [1, 3, 6, 2, -1, 5, 1, 3, 0],
  name: "main",
  legendgroup: "main"
}];
var fillers = dataToTraces(data[0], 2);

Plotly.newPlot("myDiv", data.concat(fillers));
<div id="myDiv"></div>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

A solution is to use multiple traces.
Split all your traces between ones which are above 0 and ones which are not.
When you are done you can fill them (or not) with the 'tozeroy' value.

The following jsfiddle shows a working example.

The code is as following :

HTML:

<div id="myDiv" style="width:600px;height:250px;"></div>

JS:

var data = [
  {
    x: ['A', 'B', 'C', 'D'],
    y: [1, 3, 6, 0],
    fill: 'tozeroy',
    fillcolor: '#8adcb3'
  },
  {
    x: ['D', 'F', 'G', 'I'],
    y: [0, -3, -2, 0],
    fill: 'toself'
  },
   {
    x: ['I', 'J', 'K'],
    y: [0, 5, 7],
    fill: 'tozeroy',
    fillcolor: '#0adcb3'
  }
];

Plotly.newPlot('myDiv', data);

The result looks as following :
Result plot