Search code examples
javascripthighchartsxrange

Highcharts xranges draw on wrong y value


I am encountering unexpected behavior when drawing additional xranges to my Highchart on single x- and y-axes.

Upon initial start of dragging the xrange, I want hypothetical drop spots for the dragged xrange to appear.

Upon drop, I want the xrange to jump to the hypothetical drop point closest to the actual drop point and the hypothetical drop spots to disappear.

I am trying to achieve this by temporarily drawing additional xranges at the values for those hypothetical drop spots, which I am again removing upon drop.

I would expect the newly added xranges to appear right on the tick of their assigned y-value. However, they appear to have an offset of a few decimal points (same with the updated y-value of the initial xrange in the example below - it somehow works in my actual project though).

To replicate, run the code below and drag & drop the xrange.

(Line series can be ignored, just placed it there to fix the ranges.)

const ghosts = [1, 2, 3, 4, 5];

const chart = Highcharts.chart('container', {
    legend: {
        enabled: false
    },
    series: [{
    type: 'line',
    data: [
        [0, 0],
      [1, 1],
      [2, 2],
      [3, 3],
      [4, 4],
      [5, 5],
      [6, 6]
    ]
  }],
  chart: {
    events: {
      load: function() {
        addXRange(this);
      }
    }
  }
});

function addXRange(chart) {
    chart.addSeries({
        type: 'xrange',
        pointWidth: 3,
        threshold: null,
        data: [{
            x: 1,
            x2: 5,
            y: 3,
            draggableY: true
        }],
        dragDrop: {
            draggableY: true
        },
        point: {
            events: {
                dragStart: function(e) {
                    addGhosts(chart, ghosts);
                },
                drop: function(e) {
                    const dropPoint = e.target.options.y;
                    const closestGhost = ghosts.reduce((prev, curr) =>
                        Math.abs(curr - dropPoint) < Math.abs(prev - dropPoint) ? curr : prev
                    );
                    deleteGhosts(chart);
                    this.update({
                        y: closestGhost
                    });
                    chart.redraw(false);
                }
            }
        }
    });
}

function addGhosts(chart, ghosts) {
    const className = 'ghostClass';

    ghosts.forEach(function(ghost) {
        chart.addSeries({
            type: 'xrange',
            pointWidth: 3,
            threshold: null,
            className: className,
            data: [{
                x: 1,
                x2: 5,
                y: ghost
            }]
        });
    });
}

function deleteGhosts(chart) {
    const className = 'ghostClass';

    for (let i = chart.series.length - 1; i >= 0; i--) {
        if (chart.series[i].options.className === className) {
            chart.series[i].remove(false, false);
        }
    }
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/xrange.js"></script>
<script src="https://code.highcharts.com/modules/draggable-points.js"></script>

<div id="container"></div>


Solution

  • That's because grouping is enabled by default. Extra space is left to show all series points with the same y value. You can disable it in this way:

      plotOptions: {
        xrange: {
          grouping: false
        }
      }
    

    Live demo: https://jsfiddle.net/BlackLabel/nyuog4k0/

    API Reference: https://api.highcharts.com/highcharts/series.xrange.grouping