Sondre
Sondre

Reputation: 1898

Making a dataLabel draggable

In highcharts I'm trying to add a drag-effect to point's datalabel. I've found a couple of ways to target a given label. In this fiddle you can see some of my attempts. (I've combined to different attempts in one fiddle here to show off what I've tried)

http://jsfiddle.net/EjwaX/3/

Now both using chart.series[0].data[0].dataLabel and using the points click event makes me able to change the attributes of a dataLabel. However I'm not able to attach a jquery ui .draggable() to the label. Is is somehow possible to make an element inside a chart draggable?

Please let me know if any more information is needed

Upvotes: 4

Views: 1557

Answers (2)

Frédéric Hamidi
Frédéric Hamidi

Reputation: 262969

You can actually attach draggable widgets to the data labels just fine. However, since the data labels are SVG elements, they completely ignore changes made to their position, left and top CSS properties.

Instead, the <g> elements that wrap the labels expose a transform attribute:

<g zIndex="1" transform="translate(9, 152)">
    <!-- data label content -->
</g>

We can still use the draggable widget (which is quite handy because it abstracts the lower-level drag events) and update the transform attribute of the data label wrappers in a drag event handler. Since drag coordinates are relative, we'll have to parse the original transform attribute and use the resulting coordinates as the origin.

Now, all the label wrappers are themselves children of groups decorated with the highcharts-data-labels class. This provides us with a good starting point, so we can write something like:

$(".highcharts-data-labels").children().filter(function() {
    var $this = $(this);
    // Parse original translation.
    var coords = $this.attr("transform")
                      .match(/translate\((-?\d+),\s*(-?\d+)\)/);
    if (coords) {
        // Associate origin with element.
        return $this.data("origin", {
            x: +coords[1],
            y: +coords[2]
        });
    } else {
        // Parsing failed, reject element.
        return false;
    }
}).draggable({
    drag: function(event, ui) {
        var $this = $(this);
        // Fetch back origin.
        var origin = $this.data("origin");
        // Update current transform.
        $this.attr("transform", "translate("
            + (origin.x + ui.position.left) + ", "
            + (origin.y + ui.position.top) + ")");
    }
});

You will find an updated fiddle here.

A few remarks about the code above:

  • filter() is used instead of each() because it allows us to filter out elements whose transform attributes could not be parsed (you never know), instead of outright failing or feeding garbage to the computations in the drag handler,

  • attr() is used instead of prop() to emphasize the fact we're modifying attributes in an SVG fragment. prop() would probably work in the exact same way, so it's only a matter of style and personal preference.

Finally, a caveat: I can only test this on Firefox for now, but it seems to be very difficult to trigger mousedown events on some of the labels, especially in "crowded" areas with portions of the curve and the data tip competing for space. Tweaking the zIndex attributes of the data label wrappers did not achieve much. Multiple clicks sometimes help. If this is consistent on all browsers, additional solutions will probably have to be devised (transparent overlays maybe, or simply relaying mousedown events from the data points themselves to the data labels).

Upvotes: 1

Mina Gabriel
Mina Gabriel

Reputation: 25090

working jsFiddle

$(document).ready(function() {
    chart = new Highcharts.Chart({
        chart: {
            renderTo: 'container',
            type: 'line'
        },
        title: {
            text: 'Monthly Average Temperature'
        },
        subtitle: {
            text: 'Source: WorldClimate.com'
        },
        xAxis: {
            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        },
        yAxis: {
            title: {
                text: 'Temperature'
            },
            plotLines: [{
                value: 0,
                width: 1,
                color: '#808080'
            }]
        },
        plotOptions: {
            series: {
                dataLabels: {
                    enabled: true
                },
                events: {
                    //Here I'm able to move the labels by using the click and shift-click. 
                    //I wish to do this by dragging
                    click: function(event) {
                        var current_y = event.point.dataLabel.attr('y');                        
                        if(event.shiftKey) {
                            var new_y = current_y + 2;
                            event.point.dataLabel.attr({y: new_y});
                        } else {
                            var new_y = current_y - 2;
                            event.point.dataLabel.attr({y: new_y});                            
                        }
                    }
                }                    
            }
        },
        series: [{
            name: 'Tokyo',
            data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
        }, {
            name: 'New York',
            data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]
        }, {
            name: 'Berlin',
            data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]
        }]
    });

    //Second attempt to grab a point. Works for chaning attributes
    var point = chart.series[0].data[0].dataLabel;


} , function(chart){

});

 $(function() {
   console.log(chart) ;
       $( "#draggable" ).draggable()
       .text(chart.series[0].data[0].y + 'drag and drop me     ');
     });

Upvotes: 0

Related Questions