diff --git a/samples/legend/callbacks.html b/samples/legend/callbacks.html
new file mode 100644
index 00000000000..bfb7f831005
--- /dev/null
+++ b/samples/legend/callbacks.html
@@ -0,0 +1,131 @@
+
+
+
+
+ Line Chart
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/samples.js b/samples/samples.js
index b6ffe762682..b0f8005908c 100644
--- a/samples/samples.js
+++ b/samples/samples.js
@@ -148,6 +148,9 @@
}, {
title: 'Point style',
path: 'legend/point-style.html'
+ }, {
+ title: 'Callbacks',
+ path: 'legend/callbacks.html'
}]
}, {
title: 'Tooltip',
diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js
index 7d13ea13ed4..81080d26d17 100644
--- a/src/plugins/plugin.legend.js
+++ b/src/plugins/plugin.legend.js
@@ -30,6 +30,7 @@ defaults._set('global', {
},
onHover: null,
+ onLeave: null,
labels: {
boxWidth: 40,
@@ -106,6 +107,9 @@ var Legend = Element.extend({
// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
+ // Contains the currently hovered legend item
+ this.hoveredItem = null;
+
// Are we in doughnut mode which has a different data type
this.doughnutMode = false;
},
@@ -472,7 +476,7 @@ var Legend = Element.extend({
var changed = false;
if (type === 'mousemove') {
- if (!opts.onHover) {
+ if (!opts.onHover && !opts.onLeave) {
return;
}
} else if (type === 'click') {
@@ -486,6 +490,7 @@ var Legend = Element.extend({
// Chart event already has relative position in it
var x = e.x;
var y = e.y;
+ var hoveredItem = null;
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
// See if we are touching one of the dataset boxes
@@ -495,21 +500,33 @@ var Legend = Element.extend({
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
// Touching an element
+ hoveredItem = me.legendItems[i];
+
if (type === 'click') {
// use e.native for backwards compatibility
opts.onClick.call(me, e.native, me.legendItems[i]);
changed = true;
break;
} else if (type === 'mousemove') {
- // use e.native for backwards compatibility
- opts.onHover.call(me, e.native, me.legendItems[i]);
- changed = true;
+ if (opts.onHover) {
+ // use e.native for backwards compatibility
+ opts.onHover.call(me, e.native, me.legendItems[i]);
+ changed = true;
+ }
break;
}
}
}
}
+ if (type === 'mousemove' && me.hoveredItem !== hoveredItem) {
+ if (me.hoveredItem) {
+ opts.onLeave.call(me, e.native, me.hoveredItem);
+ changed = true;
+ }
+ me.hoveredItem = hoveredItem;
+ }
+
return changed;
}
});
diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js
index fee715bdb13..e970c596a82 100644
--- a/test/specs/plugin.legend.tests.js
+++ b/test/specs/plugin.legend.tests.js
@@ -11,6 +11,7 @@ describe('Legend block tests', function() {
// a callback that will handle
onClick: jasmine.any(Function),
onHover: null,
+ onLeave: null,
labels: {
boxWidth: 40,
@@ -653,4 +654,53 @@ describe('Legend block tests', function() {
expect(chart.legend.options).toEqual(jasmine.objectContaining(Chart.defaults.global.legend));
});
});
+
+ describe('callbacks', function() {
+ it('should call onClick, onHover and onLeave at the correct times', function() {
+ var clickItem = null;
+ var hoverItem = null;
+ var leaveItem = null;
+
+ var chart = acquireChart({
+ type: 'line',
+ data: {
+ labels: ['A', 'B', 'C', 'D'],
+ datasets: [{
+ data: [10, 20, 30, 100]
+ }]
+ },
+ options: {
+ legend: {
+ onClick: function(_, item) {
+ clickItem = item;
+ },
+ onHover: function(_, item) {
+ hoverItem = item;
+ },
+ onLeave: function(_, item) {
+ leaveItem = item;
+ }
+ }
+ }
+ });
+
+ var hb = chart.legend.legendHitBoxes[0];
+ var el = {
+ x: hb.left + (hb.width / 2),
+ y: hb.top + (hb.height / 2)
+ };
+
+ jasmine.triggerMouseEvent(chart, 'click', el);
+
+ expect(clickItem).toBe(chart.legend.legendItems[0]);
+
+ jasmine.triggerMouseEvent(chart, 'mousemove', el);
+
+ expect(hoverItem).toBe(chart.legend.legendItems[0]);
+
+ jasmine.triggerMouseEvent(chart, 'mousemove', chart.getDatasetMeta(0).data[0]);
+
+ expect(leaveItem).toBe(chart.legend.legendItems[0]);
+ });
+ });
});