Provide APIs to set active (hovered) and tooltip elements.
Chart.setActiveElements will set the hovered items.
Chart.tooltip.setActiveElements will set the tooltip items.
chart.show(1); // shows dataset at index 1 and does 'show' animation.
```
+## setActiveElements(activeElements)
+
+Sets the active (hovered) elements for the chart. See the "Programmatic Events" sample file to see this in action.
+
+```javascript
+chart.setActiveElements([
+ {datasetIndex: 0, index: 1},
+]);
+```
+
## Static: getChart(key)
Finds the chart instance from the given key. If the key is a `string`, it is interpreted as the ID of the Canvas node for the Chart. The key can also be a `CanvasRenderingContext2D` or an `HTMLDOMElement`. This will return `undefined` if no Chart is found. To be found, the chart must have previously been created.
--- /dev/null
+<!doctype html>
+<html>
+
+<head>
+ <title>Programmatic Event Triggers</title>
+ <script src="../../dist/chart.js"></script>
+ <script src="../utils.js"></script>
+ <style>
+ canvas {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+ </style>
+</head>
+
+<body>
+ <div id="container" style="width: 75%;">
+ <canvas id="canvas"></canvas>
+ </div>
+ <button id="hover">Trigger Hover</button>
+ <button id="tooltip">Trigger Tooltip</button>
+ <script>
+ var color = Chart.helpers.color;
+ var barChartData = {
+ labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
+ datasets: [{
+ label: 'Dataset 1',
+ backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
+ borderColor: window.chartColors.red,
+ borderWidth: 1,
+ hoverBorderWidth: 5,
+ hoverBorderColor: 'green',
+ data: [
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor()
+ ]
+ }, {
+ label: 'Dataset 2',
+ backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
+ borderColor: window.chartColors.blue,
+ borderWidth: 1,
+ hoverBorderWidth: 5,
+ hoverBorderColor: 'green',
+ data: [
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor(),
+ randomScalingFactor()
+ ]
+ }]
+
+ };
+
+ window.onload = function() {
+ var ctx = document.getElementById('canvas').getContext('2d');
+ window.myBar = new Chart(ctx, {
+ type: 'bar',
+ data: barChartData,
+ options: {
+ responsive: true,
+ }
+ });
+
+ };
+
+ document.getElementById('hover').addEventListener('click', function() {
+ if (window.myBar.getActiveElements().length > 0) {
+ window.myBar.setActiveElements([]);
+ } else {
+ window.myBar.setActiveElements(
+ [
+ {
+ datasetIndex: 0,
+ index: 0,
+ }, {
+ datasetIndex: 1,
+ index: 0,
+ }
+ ]);
+ }
+ window.myBar.update();
+ });
+
+ document.getElementById('tooltip').addEventListener('click', function() {
+ const tooltip = window.myBar.tooltip;
+ if (tooltip.getActiveElements().length > 0) {
+ tooltip.setActiveElements([], {x: 0, y: 0});
+ } else {
+ const chartArea = window.myBar.chartArea;
+ tooltip.setActiveElements(
+ [
+ {
+ datasetIndex: 0,
+ index: 2,
+ }, {
+ datasetIndex: 1,
+ index: 2,
+ }
+ ],
+ {
+ x: (chartArea.left + chartArea.right) / 2,
+ y: (chartArea.top + chartArea.bottom) / 2,
+ }
+ );
+ }
+
+ window.myBar.update();
+ });
+ </script>
+</body>
+</html>
}, {
title: 'Line Gradient',
path: 'advanced/line-gradient.html'
+ }, {
+ title: 'Programmatic Event Triggers',
+ path: 'advanced/programmatic-events.html'
}]
}];
}
}
+ /**
+ * Get active (hovered) elements
+ * @returns array
+ */
+ getActiveElements() {
+ return this._active || [];
+ }
+
+ /**
+ * Set active (hovered) elements
+ * @param {array} activeElements New active data points
+ */
+ setActiveElements(activeElements) {
+ const me = this;
+ const lastActive = me._active || [];
+ const active = activeElements.map(({datasetIndex, index}) => {
+ const meta = me.getDatasetMeta(datasetIndex);
+ if (!meta) {
+ throw new Error('No dataset found at index ' + datasetIndex);
+ }
+
+ return {
+ datasetIndex,
+ element: meta.data[index],
+ index,
+ };
+ });
+ const changed = !_elementsEqual(active, lastActive);
+
+ if (changed) {
+ me._active = active;
+ me._updateHoverStyles(active, lastActive);
+ }
+ }
+
/**
* @private
*/
}
}
+ /**
+ * Get active elements in the tooltip
+ * @returns {Array} Array of elements that are active in the tooltip
+ */
+ getActiveElements() {
+ return this._active || [];
+ }
+
+ /**
+ * Set active elements in the tooltip
+ * @param {array} activeElements Array of active datasetIndex/index pairs.
+ * @param {object} eventPosition Synthetic event position used in positioning
+ */
+ setActiveElements(activeElements, eventPosition) {
+ const me = this;
+ const lastActive = me._active;
+ const active = activeElements.map(({datasetIndex, index}) => {
+ const meta = me._chart.getDatasetMeta(datasetIndex);
+
+ if (!meta) {
+ throw new Error('Cannot find a dataset at index ' + datasetIndex);
+ }
+
+ return {
+ datasetIndex,
+ element: meta.data[index],
+ index,
+ };
+ });
+ const changed = !_elementsEqual(lastActive, active);
+ const positionChanged = me._positionChanged(active, eventPosition);
+
+ if (changed || positionChanged) {
+ me._active = active;
+ me._eventPosition = eventPosition;
+ me.update(true);
+ }
+ }
+
/**
* Handle an event
* @param {IEvent} e - The event to handle
// When there are multiple items shown, but the tooltip position is nearest mode
// an update may need to be made because our position may have changed even though
// the items are the same as before.
- const position = positioners[options.position].call(me, active, e);
- const positionChanged = this.caretX !== position.x || this.caretY !== position.y;
+ const positionChanged = me._positionChanged(active, e);
// Remember Last Actives
changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
return changed;
}
+
+ /**
+ * Determine if the active elements + event combination changes the
+ * tooltip position
+ * @param {array} active - Active elements
+ * @param {IEvent} e - Event that triggered the position change
+ * @returns {boolean} True if the position has changed
+ */
+ _positionChanged(active, e) {
+ const me = this;
+ const position = positioners[me.options.position].call(me, active, e);
+ return me.caretX !== position.x || me.caretY !== position.y;
+ }
}
/**
expect(Chart.getChart(1)).toBeUndefined();
});
});
+
+ describe('active elements', function() {
+ it('should set the active elements', function() {
+ var chart = acquireChart({
+ type: 'pie',
+ data: {
+ datasets: [{
+ data: [1, 2, 3],
+ borderColor: 'red',
+ hoverBorderColor: 'blue',
+ }]
+ }
+ });
+
+ const meta = chart.getDatasetMeta(0);
+ let props = meta.data[0].getProps(['borderColor']);
+ expect(props.options.borderColor).toEqual('red');
+
+ chart.setActiveElements([{
+ datasetIndex: 0,
+ index: 0,
+ }]);
+
+ props = meta.data[0].getProps(['borderColor']);
+ expect(props.options.borderColor).toEqual('blue');
+
+ const active = chart.getActiveElements();
+ expect(active.length).toEqual(1);
+ expect(active[0].element).toBe(meta.data[0]);
+ });
+ });
});
]));
});
});
+
+ describe('active events', function() {
+ it('should set the active events', function() {
+ var chart = window.acquireChart({
+ type: 'line',
+ data: {
+ datasets: [{
+ label: 'Dataset 1',
+ data: [10, 20, 30],
+ pointHoverBorderColor: 'rgb(255, 0, 0)',
+ pointHoverBackgroundColor: 'rgb(0, 255, 0)'
+ }],
+ labels: ['Point 1', 'Point 2', 'Point 3']
+ },
+ });
+
+ const meta = chart.getDatasetMeta(0);
+ chart.tooltip.setActiveElements([{datasetIndex: 0, index: 0}], {x: 0, y: 0});
+ expect(chart.tooltip.getActiveElements()[0].element).toBe(meta.data[0]);
+ });
+ });
});
| false;
}
+export interface ActiveDataPoint {
+ datasetIndex: number;
+ index: number;
+}
+
+export interface ActiveElement extends ActiveDataPoint {
+ element: Element;
+}
+
export declare class Chart<
TYPE extends IChartType = IChartType,
DATA extends unknown[] = DefaultDataPoint<TYPE>,
hide(datasetIndex: number): void;
show(datasetIndex: number): void;
+ getActiveElements(): ActiveElement[];
+ setActiveElements(active: ActiveDataPoint[]);
+
destroy(): void;
toBase64Image(type?: string, quality?: any): string;
bindEvents(): void;
-import { Chart, Element, IAnimationSpecContainer, InteractionMode, LayoutPosition, IPlugin } from '../core';
+import { ActiveDataPoint, ActiveElement, Chart, Element, IAnimationSpecContainer, InteractionMode, LayoutPosition, IPlugin } from '../core';
import { Color, IChartArea, IFontSpec, Scriptable, TextAlign, IEvent, IHoverInteractionOptions } from '../core/interfaces';
import { PointStyle } from '../elements';
import { IChartData, IChartDataset } from '../interfaces';
readonly positioners: {
[key: string]: (items: readonly Element[], eventPosition: { x: number; y: number }) => { x: number; y: number };
};
+
+ getActiveElements(): ActiveElement[];
+ setActiveElements(active: ActiveDataPoint[], eventPosition: { x: number, y: number });
};
export interface ITooltipCallbacks {