Skip to content

Commit 62b57dd

Browse files
committed
Add onLeave callback to legend
1 parent 0ed652b commit 62b57dd

File tree

4 files changed

+204
-4
lines changed

4 files changed

+204
-4
lines changed

samples/legend/callbacks.html

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<!doctype html>
2+
<html>
3+
4+
<head>
5+
<title>Line Chart</title>
6+
<script src="../../../dist/Chart.min.js"></script>
7+
<script src="../utils.js"></script>
8+
<style>
9+
body, html {
10+
height: 100%;
11+
font-family: sans-serif;
12+
}
13+
canvas{
14+
-moz-user-select: none;
15+
-webkit-user-select: none;
16+
-ms-user-select: none;
17+
}
18+
19+
#log {
20+
position: absolute;
21+
right: 0;
22+
top: 0;
23+
bottom: 0;
24+
background-color: #EEE;
25+
float: right;
26+
width: 20%;
27+
padding: 8px;
28+
overflow-y: auto;
29+
white-space: pre;
30+
line-height: 1.5em;
31+
}
32+
</style>
33+
</head>
34+
35+
<body>
36+
<div id="log"></div>
37+
<div style="width:75%;">
38+
<canvas id="canvas"></canvas>
39+
</div>
40+
<script>
41+
var logEntry = 1;
42+
var logElement = document.getElementById('log');
43+
44+
function log(text) {
45+
logElement.innerText += logEntry + '. ' + text + '\n';
46+
logElement.scrollTop = logElement.scrollHeight;
47+
logEntry++;
48+
}
49+
50+
var config = {
51+
type: 'line',
52+
data: {
53+
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
54+
datasets: [{
55+
label: 'My First dataset',
56+
backgroundColor: window.chartColors.red,
57+
borderColor: window.chartColors.red,
58+
data: [
59+
randomScalingFactor(),
60+
randomScalingFactor(),
61+
randomScalingFactor(),
62+
randomScalingFactor(),
63+
randomScalingFactor(),
64+
randomScalingFactor(),
65+
randomScalingFactor()
66+
],
67+
fill: false,
68+
}, {
69+
label: 'My Second dataset',
70+
fill: false,
71+
backgroundColor: window.chartColors.blue,
72+
borderColor: window.chartColors.blue,
73+
data: [
74+
randomScalingFactor(),
75+
randomScalingFactor(),
76+
randomScalingFactor(),
77+
randomScalingFactor(),
78+
randomScalingFactor(),
79+
randomScalingFactor(),
80+
randomScalingFactor()
81+
],
82+
}]
83+
},
84+
options: {
85+
legend: {
86+
onHover: function(event, legendItem) {
87+
log('onHover: ' + legendItem.text);
88+
},
89+
onLeave: function(event, legendItem) {
90+
log('onLeave: ' + legendItem.text);
91+
},
92+
onClick: function(event, legendItem) {
93+
log('onClick:' + legendItem.text);
94+
}
95+
},
96+
responsive: true,
97+
title: {
98+
display: true,
99+
text: 'Chart.js Line Chart'
100+
},
101+
hover: {
102+
mode: 'nearest',
103+
intersect: true
104+
},
105+
scales: {
106+
xAxes: [{
107+
display: true,
108+
scaleLabel: {
109+
display: true,
110+
labelString: 'Month'
111+
}
112+
}],
113+
yAxes: [{
114+
display: true,
115+
scaleLabel: {
116+
display: true,
117+
labelString: 'Value'
118+
}
119+
}]
120+
}
121+
}
122+
};
123+
124+
window.onload = function() {
125+
var ctx = document.getElementById('canvas').getContext('2d');
126+
window.myLine = new Chart(ctx, config);
127+
};
128+
</script>
129+
</body>
130+
131+
</html>

samples/samples.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@
148148
}, {
149149
title: 'Point style',
150150
path: 'legend/point-style.html'
151+
}, {
152+
title: 'Callbacks',
153+
path: 'legend/callbacks.html'
151154
}]
152155
}, {
153156
title: 'Tooltip',

src/plugins/plugin.legend.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ defaults._set('global', {
3030
},
3131

3232
onHover: null,
33+
onLeave: null,
3334

3435
labels: {
3536
boxWidth: 40,
@@ -106,6 +107,9 @@ var Legend = Element.extend({
106107
// Contains hit boxes for each dataset (in dataset order)
107108
this.legendHitBoxes = [];
108109

110+
// Contains the currently hovered legend item
111+
this.hoveredItem = null;
112+
109113
// Are we in doughnut mode which has a different data type
110114
this.doughnutMode = false;
111115
},
@@ -472,7 +476,7 @@ var Legend = Element.extend({
472476
var changed = false;
473477

474478
if (type === 'mousemove') {
475-
if (!opts.onHover) {
479+
if (!opts.onHover && !opts.onLeave) {
476480
return;
477481
}
478482
} else if (type === 'click') {
@@ -486,6 +490,7 @@ var Legend = Element.extend({
486490
// Chart event already has relative position in it
487491
var x = e.x;
488492
var y = e.y;
493+
var hoveredItem = null;
489494

490495
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
491496
// See if we are touching one of the dataset boxes
@@ -495,21 +500,33 @@ var Legend = Element.extend({
495500

496501
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
497502
// Touching an element
503+
hoveredItem = me.legendItems[i];
504+
498505
if (type === 'click') {
499506
// use e.native for backwards compatibility
500507
opts.onClick.call(me, e.native, me.legendItems[i]);
501508
changed = true;
502509
break;
503510
} else if (type === 'mousemove') {
504-
// use e.native for backwards compatibility
505-
opts.onHover.call(me, e.native, me.legendItems[i]);
506-
changed = true;
511+
if (opts.onHover) {
512+
// use e.native for backwards compatibility
513+
opts.onHover.call(me, e.native, me.legendItems[i]);
514+
changed = true;
515+
}
507516
break;
508517
}
509518
}
510519
}
511520
}
512521

522+
if (type === 'mousemove' && me.hoveredItem !== hoveredItem) {
523+
if (me.hoveredItem) {
524+
opts.onLeave.call(me, e.native, me.hoveredItem);
525+
changed = true;
526+
}
527+
me.hoveredItem = hoveredItem;
528+
}
529+
513530
return changed;
514531
}
515532
});

test/specs/plugin.legend.tests.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,4 +653,53 @@ describe('Legend block tests', function() {
653653
expect(chart.legend.options).toEqual(jasmine.objectContaining(Chart.defaults.global.legend));
654654
});
655655
});
656+
657+
describe('callbacks', function() {
658+
it('should call onClick, onHover and onLeave at the correct times', function() {
659+
var clickItem = null;
660+
var hoverItem = null;
661+
var leaveItem = null;
662+
663+
var chart = acquireChart({
664+
type: 'line',
665+
data: {
666+
labels: ['A', 'B', 'C', 'D'],
667+
datasets: [{
668+
data: [10, 20, 30, 100]
669+
}]
670+
},
671+
options: {
672+
legend: {
673+
onClick: function(_, item) {
674+
clickItem = item;
675+
},
676+
onHover: function(_, item) {
677+
hoverItem = item;
678+
},
679+
onLeave: function(_, item) {
680+
leaveItem = item;
681+
}
682+
}
683+
}
684+
});
685+
686+
var hb = chart.legend.legendHitBoxes[0];
687+
var el = {
688+
x: hb.left + (hb.width / 2),
689+
y: hb.top + (hb.height / 2)
690+
};
691+
692+
jasmine.triggerMouseEvent(chart, 'click', el);
693+
694+
expect(clickItem).toBe(chart.legend.legendItems[0]);
695+
696+
jasmine.triggerMouseEvent(chart, 'mousemove', el);
697+
698+
expect(hoverItem).toBe(chart.legend.legendItems[0]);
699+
700+
jasmine.triggerMouseEvent(chart, 'mousemove', chart.getDatasetMeta(0).data[0]);
701+
702+
expect(leaveItem).toBe(chart.legend.legendItems[0]);
703+
});
704+
});
656705
});

0 commit comments

Comments
 (0)